├── requirements.txt ├── rutetider (separate pypi) ├── __init__.py ├── rutetider_utils.py └── rutetider.py ├── .travis.yml ├── rutetider (django, api) ├── rutetider_utils.py ├── urls.py ├── rutetider.py └── views.py ├── setup.py ├── LICENSE.md ├── .gitignore └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2==2.7.1 -------------------------------------------------------------------------------- /rutetider (separate pypi)/__init__.py: -------------------------------------------------------------------------------- 1 | from .rutetider import Timetable, Subscribers, CurrentDates, UserPosition, Statistics -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "3.4" 5 | - "3.5" 6 | install: 7 | - "pip install -r requirements.txt" 8 | - "pip install ." 9 | - "pip install nose" 10 | script: nosetests -------------------------------------------------------------------------------- /rutetider (django, api)/rutetider_utils.py: -------------------------------------------------------------------------------- 1 | def database_url_parse(url): 2 | url = url.replace('postgres://', '').replace('@', ' ').replace(':', ' ').replace('/', ' ').split() 3 | 4 | database_url = {} 5 | 6 | for part, credential in zip(range(len(url)), ['user', 'password', 'host', 'post', 'database']): 7 | database_url[credential] = url[part] 8 | 9 | return database_url 10 | -------------------------------------------------------------------------------- /rutetider (separate pypi)/rutetider_utils.py: -------------------------------------------------------------------------------- 1 | def database_url_parse(url): 2 | url = url.replace('postgres://', '').replace('@', ' ').replace(':', ' ').replace('/', ' ').split() 3 | 4 | database_url = {} 5 | 6 | for part, credential in zip(range(len(url)), ['user', 'password', 'host', 'post', 'database']): 7 | database_url[credential] = url[part] 8 | 9 | return database_url 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | @author: DmytryiStriletskyi 6 | @contact: dmytryi.striletskyi@gmail.com 7 | @license: MIT License 8 | Copyright (C) 2017 9 | """ 10 | 11 | try: 12 | from setuptools import setup 13 | except ImportError: 14 | from distutils.core import setup 15 | 16 | setup( 17 | name='rutetider', 18 | version='1.0.0', 19 | author='DmytryiStriletskyi', 20 | author_email='dmytryi.striletskyi@gmail.com', 21 | url='https://github.com/DmytryiStriletskyi/rutetider', 22 | description='SQL-module for Rutetider', 23 | download_url='https://github.com/DmytryiStriletskyi/rutetider/archive/master.zip', 24 | license='MIT', 25 | 26 | packages=['rutetider (separate pypi)'], 27 | install_requires=['psycopg2'], 28 | 29 | classifiers=[ 30 | 'License :: OSI Approved :: MIT License', 31 | 'Programming Language :: Python :: 2.7', 32 | 'Programming Language :: Python :: 3.4', 33 | 'Programming Language :: Python :: 3.5', 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 DmytryiStriletskyi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | MANIFEST 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # IPython Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # dotenv 82 | .env 83 | 84 | # virtualenv 85 | venv/ 86 | ENV/ 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | 94 | .idea/ -------------------------------------------------------------------------------- /rutetider (django, api)/urls.py: -------------------------------------------------------------------------------- 1 | from django.views.generic import RedirectView 2 | from frameworkrequests import views 3 | from django.conf.urls import url 4 | 5 | 6 | urlpatterns = [ 7 | 8 | url(r'^$', RedirectView.as_view(url='https://github.com/DmytryiStriletskyi/Rutetider/wiki')), 9 | 10 | # Rutetider 11 | url(r'^timetable', views.TimetableApplication.as_view()), 12 | url(r'^timetable/clear_timetable', views.ClearTimetable.as_view()), 13 | url(r'^timetable/add_lesson', views.AddLesson.as_view()), 14 | url(r'^timetable/get_lesson', views.GetLessons.as_view()), 15 | url(r'^timetable/get_all_courses', views.GetAllCourses.as_view()), 16 | url(r'^timetable/get_all_groups', views.GetAllGroups.as_view()), 17 | 18 | # Subscribers 19 | url(r'^subscribers', views.SubscribersApplication.as_view()), 20 | url(r'^subscribers/clear_subscribers', views.ClearSubscribers.as_view()), 21 | url(r'^subscribers/add_subscriber', views.AddSubscriber.as_view()), 22 | url(r'^subscribers/get_subscriber_group', views.GetSubscriberGroup.as_view()), 23 | url(r'^subscribers/is_subscriber', views.IsSubscriber.as_view()), 24 | url(r'^subscribers/unsubscribe', views.UnSubscribe.as_view()), 25 | 26 | # CurrentDates 27 | url(r'^currentdates', views.CurrentDatesApplication.as_view()), 28 | url(r'^currentdates/clear_current_dates', views.ClearCurrentDates.as_view()), 29 | url(r'^currentdates/add_current_dates', views.AddDates.as_view()), 30 | url(r'^currentdates/get_current_dates', views.GetDates.as_view()), 31 | 32 | # UserPosition 33 | url(r'^userposition', views.UserPositionApplication.as_view()), 34 | url(r'^userposition/clear_user_position', views.ClearUserPosition.as_view()), 35 | url(r'^userposition/clear_user_data', views.ClearUserData.as_view()), 36 | url(r'^userposition/set_getting_position', views.SetGettingPosition.as_view()), 37 | url(r'^userposition/set_faculty_position', views.SetFacultyPosition.as_view()), 38 | url(r'^userposition/set_course_position', views.SetCoursePosition.as_view()), 39 | url(r'^userposition/set_group_position', views.SetGroupPosition.as_view()), 40 | url(r'^userposition/get_faculty_and_course', views.GetFacultyAndCourse.as_view()), 41 | url(r'^userposition/verification', views.Verification.as_view()), 42 | url(r'^userposition/cancel_getting_started', views.CancelGettingStarted.as_view()), 43 | url(r'^userposition/cancel_faculty', views.CancelFaculty.as_view()), 44 | url(r'^userposition/cancel_course', views.CancelCourse.as_view()), 45 | url(r'^userposition/cancel_group', views.CancelGroup.as_view()), 46 | url(r'^userposition/back_keyboard', views.BackKeyboard.as_view()), 47 | 48 | # Statistic 49 | url(r'^statistics', views.StatisticsApplication.as_view()), 50 | url(r'^statistics/clear_statistics', views.ClearStatistics.as_view()), 51 | url(r'^statistics/add_statistics', views.AddStatistics.as_view()), 52 | url(r'^statistics/get_statistics_general', views.GetStatisticsGeneral.as_view()), 53 | url(r'^statistics/get_statistics_counts', views.GetStatisticsCounts.as_view()), 54 | url(r'^statistics/get_statistics_between_dates', views.GetStatisticsBetweenDates.as_view()), 55 | url(r'^statistics/get_statistics_by_point', views.GetStatisticsByPoint.as_view()), 56 | url(r'^statistics/point_between_dates', views.GetStatisticsByPointBetweenDates.as_view())] 57 | -------------------------------------------------------------------------------- /rutetider (django, api)/rutetider.py: -------------------------------------------------------------------------------- 1 | from frameworkrequests import rutetider_utils as ru 2 | import psycopg2 3 | 4 | 5 | class ConnectionExits: 6 | def __new__(cls, database_url): 7 | try: 8 | credentials = ru.database_url_parse(database_url) 9 | connection = psycopg2.connect(database=credentials['database'], user=credentials['user'], 10 | password=credentials['password'], host=credentials['host'], 11 | port=credentials['post']) 12 | connection.cursor() 13 | return connection, connection.cursor() 14 | except psycopg2.OperationalError: 15 | return False 16 | 17 | 18 | class Timetable: 19 | def __new__(cls, database_url): 20 | connection_exists = ConnectionExits(database_url) 21 | if connection_exists: 22 | return super(Timetable, cls).__new__(cls) 23 | else: 24 | return False 25 | 26 | def __init__(self, database_url): 27 | connection_exists = ConnectionExits(database_url) 28 | self.connection, self.cursor = connection_exists 29 | 30 | def create_timetable(self): 31 | self.cursor.execute("CREATE TABLE IF NOT EXISTS timetable (id serial PRIMARY KEY, faculty text, course text, \ 32 | group_name text, lesson_date text, lesson_title text, lesson_classroom text, \ 33 | lesson_order text, lesson_teacher text);") 34 | self.connection.commit() 35 | 36 | def clear_timetable(self): 37 | self.cursor.execute("DELETE FROM timetable;") 38 | self.connection.commit() 39 | 40 | def add_lesson(self, faculty, course, group_name, lesson_date, 41 | lesson_title, lesson_classroom, lesson_order, lesson_teacher): 42 | self.cursor.execute("INSERT INTO timetable (faculty, course, group_name, \ 43 | lesson_date, lesson_title, lesson_classroom, lesson_order, lesson_teacher) VALUES \ 44 | (%s, %s, %s, %s, %s, %s, %s, %s)", (faculty, course, group_name, lesson_date, lesson_title, 45 | lesson_classroom, lesson_order, lesson_teacher)) 46 | 47 | self.connection.commit() 48 | 49 | def get_lessons(self, group_name, lesson_date): 50 | self.cursor.execute("SELECT * FROM timetable WHERE group_name=%s AND lesson_date=%s", 51 | (group_name, lesson_date)) 52 | 53 | lesson, lessons = {}, {} 54 | 55 | for column in self.cursor: 56 | lesson['lesson_title'] = column[5] 57 | lesson['lesson_classroom'] = column[6] 58 | lesson['lesson_order'] = column[7] 59 | lesson['lesson_teacher'] = column[8] 60 | 61 | lessons[lesson['lesson_order']] = lesson 62 | lesson = {} 63 | 64 | return lessons 65 | 66 | def get_all_courses(self, faculty): 67 | self.cursor.execute("SELECT * FROM timetable WHERE faculty=%s", (faculty, )) 68 | return list(set([column[2] for column in self.cursor])) 69 | 70 | def get_all_groups(self, faculty, course): 71 | self.cursor.execute("SELECT * FROM timetable WHERE faculty=%s AND course=%s", (faculty, course)) 72 | return list(set([column[3] for column in self.cursor])) 73 | 74 | 75 | class Components: 76 | def __new__(cls, database_url): 77 | connection_exists = ConnectionExits(database_url) 78 | if connection_exists: 79 | return super(Components, cls).__new__(cls) 80 | else: 81 | return False 82 | 83 | def __init__(self, database_url): 84 | connection_exists = ConnectionExits(database_url) 85 | self.connection, self.cursor = connection_exists 86 | 87 | 88 | class Subscribers(Components): 89 | def __init__(self, database_url): 90 | super().__init__(database_url) 91 | 92 | def create_subscribers(self): 93 | self.cursor.execute("CREATE TABLE IF NOT EXISTS subscribers (id serial PRIMARY KEY, user_id text, \ 94 | group_name text);") 95 | self.connection.commit() 96 | 97 | def clear_subscribers(self): 98 | self.cursor.execute("DELETE FROM subscribers;") 99 | self.connection.commit() 100 | 101 | def add_subscriber(self, user_id, group_name): 102 | self.cursor.execute("INSERT INTO subscribers (user_id, group_name) VALUES (%s, %s)", (user_id, group_name)) 103 | self.connection.commit() 104 | 105 | def get_subscriber_group(self, user_id): 106 | self.cursor.execute("SELECT * FROM subscribers WHERE user_id=%s", (user_id, )) 107 | return self.cursor.fetchone()[2] 108 | 109 | def is_subscriber(self, user_id): 110 | self.cursor.execute("SELECT * FROM subscribers WHERE user_id=%s", (user_id, )) 111 | return bool(self.cursor.fetchone()) 112 | 113 | def unsubscribe(self, user_id): 114 | self.cursor.execute("DELETE FROM subscribers WHERE user_id=%s", (user_id, )) 115 | self.connection.commit() 116 | 117 | 118 | class CurrentDates(Components): 119 | def __init__(self, database_url): 120 | super().__init__(database_url) 121 | 122 | def create_current_dates(self): 123 | self.cursor.execute("CREATE TABLE IF NOT EXISTS current_dates (id serial PRIMARY KEY, \ 124 | today text, tomorrow text);") 125 | self.connection.commit() 126 | 127 | def clear_current_dates(self): 128 | self.cursor.execute("DELETE FROM current_dates;") 129 | self.connection.commit() 130 | 131 | def add_dates(self, today, tomorrow): 132 | self.cursor.execute("INSERT INTO current_dates (today, tomorrow) VALUES (%s, %s)", (today, tomorrow)) 133 | self.connection.commit() 134 | 135 | def get_dates(self): 136 | self.cursor.execute("SELECT today, tomorrow FROM current_dates WHERE id IN (SELECT max(id) \ 137 | FROM current_dates);") 138 | return self.cursor.fetchone() 139 | 140 | 141 | class UserPosition(Components): 142 | def __init__(self, database_url): 143 | super().__init__(database_url) 144 | 145 | def create_user_position(self): 146 | self.cursor.execute("CREATE TABLE IF NOT EXISTS user_position (id serial PRIMARY KEY, user_id text, \ 147 | faculty text, course text, group_name text);") 148 | self.connection.commit() 149 | 150 | def clear_user_position(self): 151 | self.cursor.execute("DELETE FROM user_position;") 152 | self.connection.commit() 153 | 154 | def clear_user_data(self, user_id): 155 | self.cursor.execute("DELETE FROM user_position WHERE user_id = %s", (user_id, )) 156 | self.connection.commit() 157 | 158 | def set_getting_position(self, user_id): 159 | self.cursor.execute("INSERT INTO user_position (user_id, faculty, course, group_name) VALUES \ 160 | (%s, %s, %s, %s)", (user_id, 'empty', 'empty', 'empty')) 161 | self.connection.commit() 162 | 163 | def set_faculty_position(self, user_id, faculty): 164 | self.cursor.execute("UPDATE user_position SET faculty = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 165 | WHERE user_id = (%s)) AND course = (%s) AND group_name = (%s)", 166 | (faculty, user_id, 'empty', 'empty')) 167 | self.connection.commit() 168 | 169 | def set_course_position(self, user_id, course): 170 | self.cursor.execute("UPDATE user_position SET course = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 171 | WHERE user_id = (%s)) AND group_name = (%s)", (course, user_id, 'empty')) 172 | self.connection.commit() 173 | 174 | def set_group_position(self, user_id, group_name): 175 | self.cursor.execute("UPDATE user_position SET group_name = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 176 | WHERE user_id = (%s)) AND group_name = (%s)", (group_name, user_id, 'empty')) 177 | self.connection.commit() 178 | 179 | def get_faculty_and_course(self, user_id): 180 | self.cursor.execute("SELECT * FROM user_position WHERE id IN (SELECT max(id) FROM user_position \ 181 | WHERE user_id = (%s))", (user_id, )) 182 | return tuple([element for index, element in enumerate(self.cursor.fetchone()) if index == 2 or index == 3]) 183 | 184 | def verification(self, user_id): 185 | self.cursor.execute("SELECT group_name FROM user_position WHERE id IN (SELECT max(id) FROM user_position \ 186 | WHERE user_id = (%s))", (user_id, )) 187 | return self.cursor.fetchone()[0] 188 | 189 | def cancel_getting_started(self, user_id): 190 | self.cursor.execute("DELETE FROM user_position WHERE user_id=%s", (user_id,)) 191 | self.connection.commit() 192 | 193 | def cancel_faculty(self, user_id): 194 | self.cursor.execute("UPDATE user_position SET faculty = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 195 | WHERE user_id = (%s))", ('empty', user_id)) 196 | self.connection.commit() 197 | 198 | def cancel_course(self, user_id): 199 | self.cursor.execute("UPDATE user_position SET course = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 200 | WHERE user_id = (%s) AND faculty != (%s))", ('empty', user_id, 'empty')) 201 | self.connection.commit() 202 | 203 | def cancel_group(self, user_id): 204 | self.cursor.execute("UPDATE user_position SET group_name = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 205 | WHERE user_id = (%s) AND faculty != (%s))", ('empty', user_id, 'empty')) 206 | self.connection.commit() 207 | 208 | def back_keyboard(self, user_id): 209 | self.cursor.execute("SELECT * FROM user_position WHERE id IN (SELECT max(id) FROM user_position \ 210 | WHERE user_id = (%s))", (user_id, )) 211 | count = -1 212 | for index, field in enumerate(self.cursor.fetchone()): 213 | if field != 'empty': 214 | count += 1 215 | return count 216 | 217 | 218 | class EstimateStatistics: 219 | def __new__(cls, database_url): 220 | connection_exists = ConnectionExits(database_url) 221 | if connection_exists: 222 | return super(EstimateStatistics, cls).__new__(cls) 223 | else: 224 | return False 225 | 226 | def __init__(self, database_url): 227 | connection_exists = ConnectionExits(database_url) 228 | self.connection, self.cursor = connection_exists 229 | 230 | def get_statistics_general(self): 231 | self.cursor.execute("SELECT * FROM statistics;") 232 | 233 | point, statistics = {}, [] 234 | 235 | for row in self.cursor: 236 | point['user_id'] = row[1] 237 | point['point'] = row[2] 238 | point['date'] = row[3] 239 | 240 | statistics.append(point) 241 | point = {} 242 | 243 | return statistics 244 | 245 | @staticmethod 246 | def get_statistics_counts(general_statistics): 247 | statistics_counts = {} 248 | for stat in general_statistics: 249 | if stat['point'] in statistics_counts: 250 | statistics_counts[stat['point']] += 1 251 | else: 252 | statistics_counts[stat['point']] = 1 253 | 254 | return statistics_counts 255 | 256 | def get_statistics_between_dates(self, general_statistics, date_from, date_to): 257 | statistics_between_dates = [stat for stat in general_statistics if date_from <= stat['date'] <= date_to] 258 | return self.get_statistics_counts(statistics_between_dates) 259 | 260 | def get_statistics_by_point(self, general_statistics, point): 261 | get_statistics_by_point = [stat for stat in general_statistics if stat['point'] == point] 262 | return self.get_statistics_counts(get_statistics_by_point) 263 | 264 | def get_statistics_by_point_between_dates(self, general_statistics, point, date_from, date_to): 265 | statistics_between_dates = [stat for stat in general_statistics if stat['point'] == point 266 | and date_from <= stat['date'] <= date_to] 267 | return self.get_statistics_counts(statistics_between_dates) 268 | 269 | 270 | class Statistics(Components): 271 | def __init__(self, database_url): 272 | super().__init__(database_url) 273 | self.get_statistics = EstimateStatistics(database_url) 274 | 275 | def create_statistics(self): 276 | self.cursor.execute("CREATE TABLE IF NOT EXISTS statistics (id serial PRIMARY KEY, user_id text, \ 277 | point text, date date);") 278 | self.connection.commit() 279 | 280 | def clear_statistics(self): 281 | self.cursor.execute("DELETE FROM statistics;") 282 | self.connection.commit() 283 | 284 | def add_statistics(self, user_id, point, date): 285 | self.cursor.execute("INSERT INTO statistics (user_id, point, date) VALUES \ 286 | (%s, %s, %s)", (user_id, point, date)) 287 | self.connection.commit() 288 | 289 | def get_statistics_general(self): 290 | return self.get_statistics.get_statistics_general() 291 | 292 | def get_statistics_counts(self): 293 | return self.get_statistics.get_statistics_counts(self.get_statistics_general()) 294 | 295 | def get_statistics_between_dates(self, date_from, date_to): 296 | return self.get_statistics.get_statistics_between_dates(self.get_statistics_general(), date_from, date_to) 297 | 298 | def get_statistics_by_point(self, point): 299 | return self.get_statistics.get_statistics_by_point(self.get_statistics_general(), point) 300 | 301 | def get_statistics_by_point_between_dates(self, point, date_from, date_to): 302 | return self.get_statistics.get_statistics_by_point_between_dates( 303 | self.get_statistics_general(), point, date_from, date_to) 304 | -------------------------------------------------------------------------------- /rutetider (separate pypi)/rutetider.py: -------------------------------------------------------------------------------- 1 | from . import rutetider_utils as ru 2 | import psycopg2 3 | 4 | class ConnectionExits: 5 | def __new__(cls, database_url): 6 | try: 7 | credentials = ru.database_url_parse(database_url) 8 | connection = psycopg2.connect(database=credentials['database'], user=credentials['user'], 9 | password=credentials['password'], host=credentials['host'], 10 | port=credentials['post']) 11 | connection.cursor() 12 | return connection, connection.cursor() 13 | except psycopg2.OperationalError: 14 | return False 15 | 16 | 17 | class Timetable: 18 | def __new__(cls, database_url): 19 | connection_exists = ConnectionExits(database_url) 20 | if connection_exists: 21 | return super(Timetable, cls).__new__(cls) 22 | else: 23 | return False 24 | 25 | def __init__(self, database_url): 26 | connection_exists = ConnectionExits(database_url) 27 | self.connection, self.cursor = connection_exists 28 | 29 | def create_timetable(self): 30 | self.cursor.execute("CREATE TABLE IF NOT EXISTS timetable (id serial PRIMARY KEY, faculty text, course text, \ 31 | group_name text, lesson_date text, lesson_title text, lesson_classroom text, \ 32 | lesson_order text, lesson_teacher text);") 33 | self.connection.commit() 34 | 35 | def clear_timetable(self): 36 | self.cursor.execute("DELETE FROM timetable;") 37 | self.connection.commit() 38 | 39 | def add_lesson(self, faculty, course, group_name, lesson_date, 40 | lesson_title, lesson_classroom, lesson_order, lesson_teacher): 41 | self.cursor.execute("INSERT INTO timetable (faculty, course, group_name, \ 42 | lesson_date, lesson_title, lesson_classroom, lesson_order, lesson_teacher) VALUES \ 43 | (%s, %s, %s, %s, %s, %s, %s, %s)", (faculty, course, group_name, lesson_date, lesson_title, 44 | lesson_classroom, lesson_order, lesson_teacher)) 45 | 46 | self.connection.commit() 47 | 48 | def get_lessons(self, group_name, lesson_date): 49 | self.cursor.execute("SELECT * FROM timetable WHERE group_name=%s AND lesson_date=%s", 50 | (group_name, lesson_date)) 51 | 52 | lesson, lessons = {}, {} 53 | 54 | for column in self.cursor: 55 | lesson['lesson_title'] = column[5] 56 | lesson['lesson_classroom'] = column[6] 57 | lesson['lesson_order'] = column[7] 58 | lesson['lesson_teacher'] = column[8] 59 | 60 | lessons[lesson['lesson_order']] = lesson 61 | lesson = {} 62 | 63 | return lessons 64 | 65 | def get_all_courses(self, faculty): 66 | self.cursor.execute("SELECT * FROM timetable WHERE faculty=%s", (faculty, )) 67 | return list(set([column[2] for column in self.cursor])) 68 | 69 | def get_all_groups(self, faculty, course): 70 | self.cursor.execute("SELECT * FROM timetable WHERE faculty=%s AND course=%s", (faculty, course)) 71 | return list(set([column[3] for column in self.cursor])) 72 | 73 | 74 | class Components: 75 | def __new__(cls, database_url): 76 | connection_exists = ConnectionExits(database_url) 77 | if connection_exists: 78 | return super(Components, cls).__new__(cls) 79 | else: 80 | return False 81 | 82 | def __init__(self, database_url): 83 | connection_exists = ConnectionExits(database_url) 84 | self.connection, self.cursor = connection_exists 85 | 86 | 87 | class Subscribers(Components): 88 | def __init__(self, database_url): 89 | super().__init__(database_url) 90 | 91 | def create_subscribers(self): 92 | self.cursor.execute("CREATE TABLE IF NOT EXISTS subscribers (id serial PRIMARY KEY, user_id text, \ 93 | group_name text);") 94 | self.connection.commit() 95 | 96 | def clear_subscribers(self): 97 | self.cursor.execute("DELETE FROM subscribers;") 98 | self.connection.commit() 99 | 100 | def add_subscriber(self, user_id, group_name): 101 | self.cursor.execute("INSERT INTO subscribers (user_id, group_name) VALUES (%s, %s)", (user_id, group_name)) 102 | self.connection.commit() 103 | 104 | def get_subscriber_group(self, user_id): 105 | self.cursor.execute("SELECT * FROM subscribers WHERE user_id=%s", (user_id, )) 106 | return self.cursor.fetchone()[2] 107 | 108 | def is_subscriber(self, user_id): 109 | self.cursor.execute("SELECT * FROM subscribers WHERE user_id=%s", (user_id, )) 110 | return bool(self.cursor.fetchone()) 111 | 112 | def unsubscribe(self, user_id): 113 | self.cursor.execute("DELETE FROM subscribers WHERE user_id=%s", (user_id, )) 114 | 115 | 116 | class CurrentDates(Components): 117 | def __init__(self, database_url): 118 | super().__init__(database_url) 119 | 120 | def create_current_dates(self): 121 | self.cursor.execute("CREATE TABLE IF NOT EXISTS current_dates (id serial PRIMARY KEY, \ 122 | today text, tomorrow text);") 123 | self.connection.commit() 124 | 125 | def clear_current_dates(self): 126 | self.cursor.execute("DELETE FROM current_dates;") 127 | self.connection.commit() 128 | 129 | def add_dates(self, today, tomorrow): 130 | self.cursor.execute("INSERT INTO current_dates (today, tomorrow) VALUES (%s, %s)", (today, tomorrow)) 131 | self.connection.commit() 132 | 133 | def get_dates(self): 134 | self.cursor.execute("SELECT today, tomorrow FROM current_dates WHERE id IN (SELECT max(id) \ 135 | FROM current_dates);") 136 | return self.cursor.fetchone() 137 | 138 | 139 | class UserPosition(Components): 140 | def __init__(self, database_url): 141 | super().__init__(database_url) 142 | 143 | def create_user_position(self): 144 | self.cursor.execute("CREATE TABLE IF NOT EXISTS user_position (id serial PRIMARY KEY, user_id text, \ 145 | faculty text, course text, group_name text);") 146 | self.connection.commit() 147 | 148 | def clear_user_position(self): 149 | self.cursor.execute("DELETE FROM user_position;") 150 | self.connection.commit() 151 | 152 | def clear_user_data(self, user_id): 153 | self.cursor.execute("DELETE FROM user_position WHERE user_id = %s", (user_id, )) 154 | self.connection.commit() 155 | 156 | def set_getting_position(self, user_id): 157 | self.cursor.execute("INSERT INTO user_position (user_id, faculty, course, group_name) VALUES \ 158 | (%s, %s, %s, %s)", (user_id, 'empty', 'empty', 'empty')) 159 | self.connection.commit() 160 | 161 | def set_faculty_position(self, user_id, faculty): 162 | self.cursor.execute("UPDATE user_position SET faculty = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 163 | WHERE user_id = (%s)) AND course = (%s) AND group_name = (%s)", 164 | (faculty, user_id, 'empty', 'empty')) 165 | self.connection.commit() 166 | 167 | def set_course_position(self, user_id, course): 168 | self.cursor.execute("UPDATE user_position SET course = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 169 | WHERE user_id = (%s)) AND group_name = (%s)", 170 | (course, user_id, 'empty')) 171 | self.connection.commit() 172 | 173 | def set_group_position(self, user_id, group_name): 174 | self.cursor.execute("UPDATE user_position SET group_name = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 175 | WHERE user_id = (%s)) AND group_name = (%s)", 176 | (group_name, user_id, 'empty')) 177 | self.connection.commit() 178 | 179 | def get_faculty_and_course(self, user_id): 180 | self.cursor.execute("SELECT * FROM user_position WHERE id IN (SELECT max(id) FROM user_position \ 181 | WHERE user_id = (%s))", 182 | (user_id, )) 183 | return tuple([element for index, element in enumerate(self.cursor.fetchone()) if index == 2 or index == 3]) 184 | 185 | def verification(self, user_id): 186 | self.cursor.execute("SELECT group_name FROM user_position WHERE id IN (SELECT max(id) FROM user_position \ 187 | WHERE user_id = (%s))", (user_id, )) 188 | return self.cursor.fetchone()[0] 189 | 190 | def cancel_getting_started(self, user_id): 191 | self.cursor.execute("DELETE FROM user_position WHERE user_id=%s", (user_id,)) 192 | self.connection.commit() 193 | 194 | def cancel_faculty(self, user_id): 195 | self.cursor.execute("UPDATE user_position SET faculty = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 196 | WHERE user_id = (%s))", ('empty', user_id)) 197 | self.connection.commit() 198 | 199 | def cancel_course(self, user_id): 200 | self.cursor.execute("UPDATE user_position SET course = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 201 | WHERE user_id = (%s) AND faculty != (%s))", ('empty', user_id, 'empty')) 202 | self.connection.commit() 203 | 204 | def cancel_group(self, user_id): 205 | self.cursor.execute("UPDATE user_position SET group_name = (%s) WHERE id IN (SELECT max(id) FROM user_position \ 206 | WHERE user_id = (%s) AND faculty != (%s))", ('empty', user_id, 'empty')) 207 | self.connection.commit() 208 | 209 | def back_keyboard(self, user_id): 210 | self.cursor.execute("SELECT * FROM user_position WHERE id IN (SELECT max(id) FROM user_position \ 211 | WHERE user_id = (%s))", (user_id, )) 212 | count = -1 213 | for index, field in enumerate(self.cursor.fetchone()): 214 | if field != 'empty': 215 | count += 1 216 | return count 217 | 218 | 219 | class EstimateStatistics: 220 | def __new__(cls, database_url): 221 | connection_exists = ConnectionExits(database_url) 222 | if connection_exists: 223 | return super(EstimateStatistics, cls).__new__(cls) 224 | else: 225 | return False 226 | 227 | def __init__(self, database_url): 228 | connection_exists = ConnectionExits(database_url) 229 | self.connection, self.cursor = connection_exists 230 | 231 | def get_statistics_general(self): 232 | self.cursor.execute("SELECT * FROM statistics;") 233 | 234 | point, statistics = {}, [] 235 | 236 | for row in self.cursor: 237 | point['user_id'] = row[1] 238 | point['point'] = row[2] 239 | point['date'] = row[3] 240 | 241 | statistics.append(point) 242 | point = {} 243 | 244 | return statistics 245 | 246 | @staticmethod 247 | def get_statistics_counts(general_statistics): 248 | statistics_counts = {} 249 | for stat in general_statistics: 250 | if stat['point'] in statistics_counts: 251 | statistics_counts[stat['point']] += 1 252 | else: 253 | statistics_counts[stat['point']] = 1 254 | 255 | return statistics_counts 256 | 257 | def get_statistics_between_dates(self, general_statistics, date_from, date_to): 258 | statistics_between_dates = [stat for stat in general_statistics if date_from <= stat['date'] <= date_to] 259 | return self.get_statistics_counts(statistics_between_dates) 260 | 261 | def get_statistics_by_point(self, general_statistics, point): 262 | get_statistics_by_point = [stat for stat in general_statistics if stat['point'] == point] 263 | return self.get_statistics_counts(get_statistics_by_point) 264 | 265 | def get_statistics_by_point_between_dates(self, general_statistics, point, date_from, date_to): 266 | statistics_between_dates = [stat for stat in general_statistics if stat['point'] == point 267 | and date_from <= stat['date'] <= date_to] 268 | return self.get_statistics_counts(statistics_between_dates) 269 | 270 | 271 | class Statistics(Components): 272 | def __init__(self, database_url): 273 | super().__init__(database_url) 274 | self.get_statistics = EstimateStatistics(database_url) 275 | 276 | def create_statistics(self): 277 | self.cursor.execute("CREATE TABLE IF NOT EXISTS statistics (id serial PRIMARY KEY, user_id text, \ 278 | point text, date date);") 279 | self.connection.commit() 280 | 281 | def clear_statistics(self): 282 | self.cursor.execute("DELETE FROM statistics;") 283 | self.connection.commit() 284 | 285 | def add_statistics(self, user_id, point, date): 286 | self.cursor.execute("INSERT INTO statistics (user_id, point, date) VALUES \ 287 | (%s, %s, %s)", (user_id, point, date)) 288 | self.connection.commit() 289 | 290 | def get_statistics_general(self): 291 | return self.get_statistics.get_statistics_general() 292 | 293 | def get_statistics_counts(self): 294 | return self.get_statistics.get_statistics_counts(self.get_statistics_general()) 295 | 296 | def get_statistics_between_dates(self, date_from, date_to): 297 | return self.get_statistics.get_statistics_between_dates(self.get_statistics_general(), date_from, date_to) 298 | 299 | def get_statistics_by_point(self, point): 300 | return self.get_statistics.get_statistics_by_point(self.get_statistics_general(), point) 301 | 302 | def get_statistics_by_point_between_dates(self, point, date_from, date_to): 303 | return self.get_statistics.get_statistics_by_point_between_dates( 304 | self.get_statistics_general(), point, date_from, date_to) 305 | -------------------------------------------------------------------------------- /rutetider (django, api)/views.py: -------------------------------------------------------------------------------- 1 | from frameworkrequests.rutetider import Timetable, Subscribers, CurrentDates, UserPosition, Statistics 2 | from rest_framework.views import APIView 3 | from django.http import JsonResponse 4 | import datetime 5 | 6 | 7 | def is_url_out(data, url): 8 | if url not in data: 9 | return True 10 | 11 | 12 | # Timetable 13 | class TimetableApplication(APIView): 14 | @staticmethod 15 | def post(request): 16 | if is_url_out(request.data, 'url'): 17 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 18 | 19 | database_url = request.data['url'] 20 | rutetider = Timetable(database_url) 21 | if rutetider: 22 | rutetider.create_timetable() 23 | return JsonResponse({ 24 | 'Response': 'Singleton application was successfully initialized.', 25 | 'Done': 'Table \'timetable\' has been created in database.', 26 | 'Additional documentation information': 'If table already exists, requests does not re-create it.'}, 27 | status=200) 28 | else: 29 | return JsonResponse({'Response': 'Singleton application creation has been failed.'}, status=400) 30 | 31 | 32 | class ClearTimetable(APIView): 33 | @staticmethod 34 | def delete(request): 35 | if is_url_out(request.data, 'url'): 36 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 37 | 38 | database_url = request.data['url'] 39 | rutetider = Timetable(database_url) 40 | if rutetider: 41 | rutetider.clear_timetable() 42 | return JsonResponse({'Response': 'Table \'timetable\' has been cleared in database.'}, status=200) 43 | else: 44 | return JsonResponse({'Response': 'Singleton application creation has been failed.'}, status=400) 45 | 46 | 47 | class AddLesson(APIView): 48 | @staticmethod 49 | def put(request): 50 | if is_url_out(request.data, 'url'): 51 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 52 | 53 | database_url = request.data['url'] 54 | rutetider = Timetable(database_url) 55 | if rutetider: 56 | rutetider.add_lesson( 57 | request.data['faculty'], request.data['course'], request.data['group_name'], 58 | request.data['lesson_date'], request.data['lesson_title'], request.data['lesson_classroom'], 59 | request.data['lesson_order'], request.data['lesson_teacher']) 60 | return JsonResponse({'Response': 'Lessons data was successfully added.'}, status=200) 61 | else: 62 | return JsonResponse({'Response': 'Lessons data addition has been failed.'}, status=400) 63 | 64 | 65 | class GetLessons(APIView): 66 | @staticmethod 67 | def post(request): 68 | if is_url_out(request.data, 'url'): 69 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 70 | 71 | database_url = request.data['url'] 72 | rutetider = Timetable(database_url) 73 | if rutetider: 74 | return JsonResponse({ 75 | 'lessons': rutetider.get_lessons(request.data['group_name'], request.data['lesson_date'])}, status=200) 76 | 77 | 78 | class GetAllCourses(APIView): 79 | @staticmethod 80 | def post(request): 81 | if is_url_out(request.data, 'url'): 82 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 83 | 84 | database_url = request.data['url'] 85 | rutetider = Timetable(database_url) 86 | if rutetider: 87 | return JsonResponse({'courses': rutetider.get_all_courses(request.data['faculty'])}, status=200) 88 | 89 | 90 | class GetAllGroups(APIView): 91 | @staticmethod 92 | def post(request): 93 | if is_url_out(request.data, 'url'): 94 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 95 | 96 | database_url = request.data['url'] 97 | rutetider = Timetable(database_url) 98 | if rutetider: 99 | return JsonResponse({'groups': rutetider.get_all_groups(request.data['faculty'], request.data['course'])}, 100 | status=200) 101 | 102 | 103 | # Subscribers 104 | class SubscribersApplication(APIView): 105 | @staticmethod 106 | def post(request): 107 | if is_url_out(request.data, 'url'): 108 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 109 | 110 | database_url = request.data['url'] 111 | subscribers = Subscribers(database_url) 112 | if subscribers: 113 | subscribers.create_subscribers() 114 | return JsonResponse({ 115 | 'Response': 'Subscribers application was successfully initialized.', 116 | 'Done': 'Table \'subscribers\' has been created in database.', 117 | 'Additional documentation information': 'If table already exists, requests does not re-create it.'}, 118 | status=200) 119 | else: 120 | return JsonResponse({'Response': 'Subscribers application creation has been failed.'}, status=400) 121 | 122 | 123 | class ClearSubscribers(APIView): 124 | @staticmethod 125 | def delete(request): 126 | if is_url_out(request.data, 'url'): 127 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 128 | 129 | database_url = request.data['url'] 130 | subscribers = Subscribers(database_url) 131 | if subscribers: 132 | subscribers.clear_subscribers() 133 | return JsonResponse({ 134 | 'Response': 'Table \'subscribers\' has been cleared in database.'}, status=200) 135 | else: 136 | return JsonResponse({'Response': 'Subscribers application creation has been failed.'}, status=400) 137 | 138 | 139 | class AddSubscriber(APIView): 140 | @staticmethod 141 | def put(request): 142 | if is_url_out(request.data, 'url'): 143 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 144 | 145 | database_url = request.data['url'] 146 | subscriber = Subscribers(database_url) 147 | if subscriber: 148 | subscriber.add_subscriber(request.data['user_id'], request.data['group_name']) 149 | return JsonResponse({'Response': 'Subscribers data was successfully added.'}, status=200) 150 | 151 | 152 | class GetSubscriberGroup(APIView): 153 | @staticmethod 154 | def post(request): 155 | if is_url_out(request.data, 'url'): 156 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 157 | database_url = request.data['url'] 158 | subscriber = Subscribers(database_url) 159 | if subscriber: 160 | return JsonResponse({'group': subscriber.get_subscriber_group(request.data['user_id'])}, status=200) 161 | else: 162 | return JsonResponse({'group': subscriber.get_subscriber_group(request.data['user_id'])}, status=400) 163 | 164 | 165 | class IsSubscriber(APIView): 166 | @staticmethod 167 | def post(request): 168 | if is_url_out(request.data, 'url'): 169 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 170 | 171 | database_url = request.data['url'] 172 | subscriber = Subscribers(database_url) 173 | if subscriber: 174 | return JsonResponse({'Is subscriber': subscriber.is_subscriber(request.data['user_id'])}, status=200) 175 | else: 176 | return JsonResponse({'Is subscriber': subscriber.is_subscriber(request.data['user_id'])}, status=400) 177 | 178 | 179 | class UnSubscribe(APIView): 180 | @staticmethod 181 | def delete(request): 182 | if is_url_out(request.data, 'url'): 183 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 184 | 185 | database_url = request.data['url'] 186 | subscriber = Subscribers(database_url) 187 | if subscriber: 188 | subscriber.unsubscribe(request.data['user_id']) 189 | return JsonResponse({'Response': 'User {0} has been deleted from table \'subscribers\''.format( 190 | request.data['user_id'])}, status=200) 191 | 192 | 193 | # CurrentDates 194 | class CurrentDatesApplication(APIView): 195 | @staticmethod 196 | def post(request): 197 | if is_url_out(request.data, 'url'): 198 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 199 | 200 | database_url = request.data['url'] 201 | dates = CurrentDates(database_url) 202 | if dates: 203 | dates.create_current_dates() 204 | return JsonResponse({ 205 | 'Response': 'CurrentDates application was successfully initialized.', 206 | 'Done': 'Table \'current_dates\' has been created in database.', 207 | 'Additional documentation information': 'If table already exists, requests does not re-create it.'}, 208 | status=200) 209 | else: 210 | return JsonResponse({'Response': 'CurrentDates application creation has been failed.'}, status=400) 211 | 212 | 213 | class ClearCurrentDates(APIView): 214 | @staticmethod 215 | def delete(request): 216 | if is_url_out(request.data, 'url'): 217 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 218 | 219 | database_url = request.data['url'] 220 | dates = CurrentDates(database_url) 221 | if dates: 222 | dates.clear_current_dates() 223 | return JsonResponse({ 224 | 'Response': 'Table \'current_dates\' has been cleared in database.'}, status=200) 225 | else: 226 | return JsonResponse({'Response': 'CurrentDates application creation has been failed.'}, status=400) 227 | 228 | 229 | class AddDates(APIView): 230 | @staticmethod 231 | def put(request): 232 | if is_url_out(request.data, 'url'): 233 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 234 | 235 | database_url = request.data['url'] 236 | dates = CurrentDates(database_url) 237 | if dates: 238 | dates.add_dates(request.data['today'], request.data['tomorrow']) 239 | return JsonResponse({'Response': 'Dates ware successfully added.'}, status=200) 240 | 241 | 242 | class GetDates(APIView): 243 | @staticmethod 244 | def post(request): 245 | if is_url_out(request.data, 'url'): 246 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 247 | 248 | database_url = request.data['url'] 249 | dates = CurrentDates(database_url) 250 | if dates: 251 | return JsonResponse({'dates': dates.get_dates()}, status=200) 252 | 253 | 254 | # UserPosition 255 | class UserPositionApplication(APIView): 256 | @staticmethod 257 | def post(request): 258 | if is_url_out(request.data, 'url'): 259 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 260 | 261 | database_url = request.data['url'] 262 | position = UserPosition(database_url) 263 | if position: 264 | position.create_user_position() 265 | return JsonResponse({ 266 | 'Response': 'UserPosition application was successfully initialized.', 267 | 'Done': 'Table \'user_position\' has been created in database.', 268 | 'Additional documentation information': 'If table already exists, requests does not re-create it.'}, 269 | status=200) 270 | else: 271 | return JsonResponse({'Response': 'UserPosition application creation has been failed.'}, status=400) 272 | 273 | 274 | class ClearUserPosition(APIView): 275 | @staticmethod 276 | def delete(request): 277 | if is_url_out(request.data, 'url'): 278 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 279 | 280 | database_url = request.data['url'] 281 | position = UserPosition(database_url) 282 | if position: 283 | position.clear_user_position() 284 | return JsonResponse({ 285 | 'Response': 'Table \'user_position\' has been cleared in database.'}, status=200) 286 | else: 287 | return JsonResponse({'Response': 'UserPosition application creation has been failed.'}, status=400) 288 | 289 | 290 | class ClearUserData(APIView): 291 | @staticmethod 292 | def delete(request): 293 | if is_url_out(request.data, 'url'): 294 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 295 | 296 | database_url = request.data['url'] 297 | position = UserPosition(database_url) 298 | if position: 299 | position.clear_user_data(request.data['user_id']) 300 | return JsonResponse({ 301 | 'Response': 'User data has been cleared from table \'user_position\' in database.'}, status=200) 302 | 303 | 304 | class SetGettingPosition(APIView): 305 | @staticmethod 306 | def put(request): 307 | if is_url_out(request.data, 'url'): 308 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 309 | 310 | database_url = request.data['url'] 311 | position = UserPosition(database_url) 312 | if position: 313 | position.set_getting_position(request.data['user_id']) 314 | return JsonResponse({'Response': 'Start position was successfully added.'}, status=200) 315 | 316 | 317 | class SetFacultyPosition(APIView): 318 | @staticmethod 319 | def put(request): 320 | if is_url_out(request.data, 'url'): 321 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 322 | 323 | database_url = request.data['url'] 324 | position = UserPosition(database_url) 325 | if position: 326 | position.set_faculty_position(request.data['user_id'], request.data['faculty']) 327 | return JsonResponse({'Response': 'Faculty position was successfully added.'}, status=200) 328 | 329 | 330 | class SetCoursePosition(APIView): 331 | @staticmethod 332 | def put(request): 333 | if is_url_out(request.data, 'url'): 334 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 335 | 336 | database_url = request.data['url'] 337 | position = UserPosition(database_url) 338 | if position: 339 | position.set_course_position(request.data['user_id'], request.data['course']) 340 | return JsonResponse({'Response': 'Course position was successfully added.'}, status=200) 341 | 342 | 343 | class SetGroupPosition(APIView): 344 | @staticmethod 345 | def put(request): 346 | if is_url_out(request.data, 'url'): 347 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 348 | 349 | database_url = request.data['url'] 350 | position = UserPosition(database_url) 351 | if position: 352 | position.set_group_position(request.data['user_id'], request.data['group_name']) 353 | return JsonResponse({'Response': 'Group position was successfully added.'}, status=200) 354 | 355 | 356 | class GetFacultyAndCourse(APIView): 357 | @staticmethod 358 | def post(request): 359 | if is_url_out(request.data, 'url'): 360 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 361 | 362 | database_url = request.data['url'] 363 | position = UserPosition(database_url) 364 | if position: 365 | return JsonResponse({'Response': position.get_faculty_and_course(request.data['user_id'])}, status=200) 366 | 367 | 368 | class Verification(APIView): 369 | @staticmethod 370 | def post(request): 371 | if is_url_out(request.data, 'url'): 372 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 373 | 374 | database_url = request.data['url'] 375 | position = UserPosition(database_url) 376 | if position: 377 | return JsonResponse({'group': position.verification(request.data['user_id'])}, status=200) 378 | 379 | 380 | class CancelGettingStarted(APIView): 381 | @staticmethod 382 | def delete(request): 383 | if is_url_out(request.data, 'url'): 384 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 385 | 386 | database_url = request.data['url'] 387 | position = UserPosition(database_url) 388 | if position: 389 | position.cancel_getting_started(request.data['user_id']) 390 | return JsonResponse({ 391 | 'Response': 'Start position has been cleared from table \'user_position\' in database.'}, status=200) 392 | 393 | 394 | class CancelFaculty(APIView): 395 | @staticmethod 396 | def put(request): 397 | if is_url_out(request.data, 'url'): 398 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 399 | 400 | database_url = request.data['url'] 401 | position = UserPosition(database_url) 402 | if position: 403 | position.cancel_faculty(request.data['user_id']) 404 | return JsonResponse({ 405 | 'Response': 'Faculty position has been cleared from table \'user_position\' in database.'}, status=200) 406 | 407 | 408 | class CancelCourse(APIView): 409 | @staticmethod 410 | def put(request): 411 | if is_url_out(request.data, 'url'): 412 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 413 | 414 | database_url = request.data['url'] 415 | position = UserPosition(database_url) 416 | if position: 417 | position.cancel_course(request.data['user_id']) 418 | return JsonResponse({ 419 | 'Response': 'Course position has been cleared from table \'user_position\' in database.'}, status=200) 420 | 421 | 422 | class CancelGroup(APIView): 423 | @staticmethod 424 | def put(request): 425 | if is_url_out(request.data, 'url'): 426 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 427 | 428 | database_url = request.data['url'] 429 | position = UserPosition(database_url) 430 | if position: 431 | position.cancel_group(request.data['user_id']) 432 | return JsonResponse({ 433 | 'Response': 'Group position has been cleared from table \'user_position\' in database.'}, status=200) 434 | 435 | 436 | class BackKeyboard(APIView): 437 | @staticmethod 438 | def post(request): 439 | if is_url_out(request.data, 'url'): 440 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 441 | 442 | database_url = request.data['url'] 443 | position = UserPosition(database_url) 444 | if position: 445 | try: 446 | return JsonResponse({'keyboard': position.back_keyboard(request.data['user_id'])}, status=200) 447 | except TypeError: 448 | return JsonResponse({'Response': 'User has no data in database'}, status=200) 449 | 450 | 451 | # Statistic 452 | class StatisticsApplication(APIView): 453 | @staticmethod 454 | def post(request): 455 | if is_url_out(request.data, 'url'): 456 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 457 | 458 | database_url = request.data['url'] 459 | statistics = Statistics(database_url) 460 | if statistics: 461 | statistics.create_statistics() 462 | return JsonResponse({ 463 | 'Response': 'Statistic application was successfully initialized.', 464 | 'Done': 'Table \'statistic\' has been created in database.', 465 | 'Additional documentation information': 'If table already exists, requests does not re-create it.'}, 466 | status=200) 467 | else: 468 | return JsonResponse({'Response': 'Statistic application creation has been failed.'}, status=400) 469 | 470 | 471 | class ClearStatistics(APIView): 472 | @staticmethod 473 | def delete(request): 474 | if is_url_out(request.data, 'url'): 475 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 476 | 477 | database_url = request.data['url'] 478 | statistics = Statistics(database_url) 479 | if statistics: 480 | statistics.clear_statistics() 481 | return JsonResponse({ 482 | 'Response': 'Table \'statistic\' has been cleared in database.'}, status=200) 483 | else: 484 | return JsonResponse({'Response': 'UserPosition application creation has been failed.'}, status=400) 485 | 486 | 487 | class AddStatistics(APIView): 488 | @staticmethod 489 | def put(request): 490 | if is_url_out(request.data, 'url'): 491 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 492 | 493 | date = datetime.datetime.strptime(request.data['date'], '%d.%m.%Y').date() 494 | 495 | database_url = request.data['url'] 496 | statistics = Statistics(database_url) 497 | if statistics: 498 | statistics.add_statistics(request.data['user_id'], request.data['point'], date) 499 | return JsonResponse({'Response': 'Statistic wars successfully added.'}, status=200) 500 | 501 | 502 | class GetStatisticsGeneral(APIView): 503 | @staticmethod 504 | def post(request): 505 | if is_url_out(request.data, 'url'): 506 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 507 | 508 | database_url = request.data['url'] 509 | statistics = Statistics(database_url) 510 | if statistics: 511 | return JsonResponse({'statistics': statistics.get_statistics_general()}, status=200) 512 | 513 | 514 | class GetStatisticsCounts(APIView): 515 | @staticmethod 516 | def post(request): 517 | if is_url_out(request.data, 'url'): 518 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 519 | 520 | database_url = request.data['url'] 521 | statistics = Statistics(database_url) 522 | if statistics: 523 | return JsonResponse({'statistics': statistics.get_statistics_counts()}, status=200) 524 | 525 | 526 | class GetStatisticsBetweenDates(APIView): 527 | @staticmethod 528 | def post(request): 529 | print('3') 530 | if is_url_out(request.data, 'url'): 531 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 532 | 533 | date_from = datetime.datetime.strptime(request.data['date_from'], '%d.%m.%Y').date() 534 | date_to = datetime.datetime.strptime(request.data['date_to'], '%d.%m.%Y').date() 535 | 536 | database_url = request.data['url'] 537 | statistics = Statistics(database_url) 538 | if statistics: 539 | return JsonResponse({'statistics': statistics.get_statistics_between_dates(date_from, date_to)}, status=200) 540 | 541 | 542 | class GetStatisticsByPoint(APIView): 543 | @staticmethod 544 | def post(request): 545 | print('2') 546 | if is_url_out(request.data, 'url'): 547 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 548 | 549 | database_url = request.data['url'] 550 | statistics = Statistics(database_url) 551 | if statistics: 552 | return JsonResponse({'statistics': statistics.get_statistics_by_point(request.data['point'])}, status=200) 553 | 554 | 555 | class GetStatisticsByPointBetweenDates(APIView): 556 | @staticmethod 557 | def post(request): 558 | print('yes') 559 | if is_url_out(request.data, 'url'): 560 | return JsonResponse({'Response': 'Database parameter URL in request data not found.'}, status=400) 561 | 562 | date_from = datetime.datetime.strptime(request.data['date_from'], '%d.%m.%Y').date() 563 | date_to = datetime.datetime.strptime(request.data['date_to'], '%d.%m.%Y').date() 564 | 565 | database_url = request.data['url'] 566 | statistics = Statistics(database_url) 567 | if statistics: 568 | return JsonResponse({'statistics': statistics.get_statistics_by_point_between_dates(request.data['point'], 569 | date_from, date_to)}, status=200) 570 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rutetider 2 | [![Build Status](https://travis-ci.org/DmytryiStriletskyi/Rutetider.svg?branch=master)](https://travis-ci.org/DmytryiStriletskyi/Rutetider) 3 | ![Python Versions](https://camo.githubusercontent.com/2337a7f5bde4563869e31ce69df4bacfc0b96277/68747470733a2f2f696d672e736869656c64732e696f2f707970692f707976657273696f6e732f74656c6567726170682e737667) 4 | 5 | ![platforms](https://habrastorage.org/files/c69/e7e/0c0/c69e7e0c07d74a0ebe3b9efdb4556cee.png) 6 | 7 | Информация подана более структурировано на русском языке [здесь](https://github.com/DmytryiStriletskyi/Rutetider/wiki/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B2-Rutetider). 8 | 9 | # **Введение в Rutetider** 10 | 11 | Rutetider — готовое архитектурное решение для всех видов платформ, поддерживающих http-запросы, в большей степени из-за отсутствия языковых и ресурсных возможностей — для веб, в частности Telegram-bot`ы, в меньшей — из-за нативной предрасположенности — для IOS и Android (часть функционала фреймворка не нужна из-за инструментов «из коробки», например, локального хранилища). 12 | 13 | Главными инструментами для разработчиков будут являтся REST-API (например, клиент на Android) и непосредственно сама библиотека, написанная на Python (Telegram-bot) — в зависимости от выбора методов и платформ разработки, а также необходима удаленная база данных (нет необходимости с ней работать, все за вас сделает «Rutetider»), бесплатную и пригодную к использованию в продакшине по объему — на этот счет не беспокойтесь, [здесь](https://github.com/DmytryiStriletskyi/Rutetider/wiki/%D0%91%D0%B0%D0%B7%D1%8B-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85-%D0%B4%D0%BB%D1%8F-%D1%80%D0%B0%D0%B7%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8) предоставлен гайд по подключению ряда бесплатных инстансов. 14 | 15 | Фреймворк имеет в наличие обязательные и необязательные модули, например, вам в любом случае необходимо работать напрямую с расписанием (заносить пары или уроки, начало и конец по времени, преподавателей и аудитории, или же наоборот — получать расписание, список групп на обределенном курсе и прочее), а вот вести статистику может быть кому-то и ни к чему. 16 | 17 | Приложение, архитектуру которого вам необходимо будет соблюдать, состоит из пяти отдельных «экранов», каждый из которого требует на определенном этапе определенных действий от разработчика, опять же — например, на основе введенной группы и даты получить из базы данных расписание на нужный день. И здесь не упущу возможности заверить, что копаться в чем-то сложном не придется, просто необходимо соблюдать шаблон и читать документацию. 18 | 19 | 20 | Первые три экрана с выбором **меню**, **факультета** и **курса**. 21 | 22 | ![first_menus](https://habrastorage.org/files/229/b09/03d/229b0903d51640c397ea8c5ae47dc930.png) 23 | 24 | Вторые два с выбором **группы** и **дат**. 25 | 26 | ![last_menus](https://habrastorage.org/files/2f6/e11/409/2f6e11409a2d4980875f0d8893c269f7.png) 27 | 28 | # **Компоненты структуры** 29 | 30 | Фреймворк предоставляет пять базовых частей, часть из них не являются обязательными, но есть основные, без которых разработка обойтись не может. 31 | 32 | Ниже представлена схема примерная схема работы приложения. Если объяснить главный принцип, то — необходимо следить и записывать на каком этапе (какое меню выбора на экране) находится пользователь, не забывать вносить данные о самом выборе (факультет, курс, группа) и отображать расписание соответсвующим входящим параметрам (получить расписание по такой-то группе и дню). 33 | 34 | ![schema](https://habrastorage.org/files/db3/3d6/deb/db33d6deb7a84cd982a2c94051a546d1.png) 35 | 36 | Все методы и детальная работа с ними будет расписана в [API по ссылке](https://github.com/DmytryiStriletskyi/Rutetider/wiki/Rutetider-API), а ниже вы можете ознакомиться чуть более глубже с тем, что, возможно, потребуется в дальнейшем. Не углубляйтесь в примеры и особенности, важно понять концепцию. 37 | 38 | ## **Timetable** 39 | 40 | Данный модуль фреймворка предназначен непосредственно для контакта с расписанием. Например, вам будет доступен вариант занисения расписания в базу данных: 41 | 42 | ```python 43 | from rutetider import Timetable 44 | 45 | timetable = Timetable(database_url) 46 | timetable.add_lesson('IT', '3', 'PD-31', '18.10', 'Литература', 47 | '451', '2', 'Шевченко Т.Г.') 48 | # params: faculty, course, group_name, lesson_date, lesson_title, 49 | # lesson_classroom, lesson_order, lesson_teacher 50 | ``` 51 | 52 | Или же вы захотите получить расписание для вашей группы на определенный день: 53 | ```python 54 | schedule = timetable.get_lessons('PD-31', '18.10') 55 | # params: group_name, lesson_date 56 | 57 | print(schedule) 58 | # {'lessons': { 59 | # '3': {'lesson_teacher': 'Шевченко О.В.', 'lesson_classroom': 60 | # '451', 'lesson_order': '3', 'lesson_title': 'Литература'}, 61 | # '1': {'lesson_teacher': 'Шульга О.С.', 'lesson_classroom': '118', 62 | # 'lesson_order': '1', 'lesson_title': #'Математика'}, 63 | # '2': {'lesson_teacher': 'Ковальчук Н.О.', 'lesson_classroom': '200', 64 | # 'lesson_order': '2', 'lesson_title': #'Инженерия ПО'}}} 65 | ``` 66 | 67 | ## **Subscribers** 68 | 69 | ![back_button](https://habrastorage.org/files/8a0/065/d6a/8a0065d6a296428e8dc86ff6269a3087.png) 70 | 71 | Вы можете имплеменировать данный компонент, который позволит пользователю совершать меньше действий и переходов, и сразу получать расписание по одной кнопке — для этого вам надо, чтобы он подписался на определенную группу (занести эту группу в базу данных), а после, при желании пользоватя получить расписание — отдать ему расписание на сегодняшний и завтрашний день (запросить группу пользователя, запросить расписание по группе и датам). 72 | 73 | ```swift 74 | import UIKit 75 | 76 | class ViewController: UIViewController { 77 | 78 | fileprivate let databaseURL = "postgres://nwritrny:VQJnfVmooh3S0TkAghEgA--YOxoaPJOR@stampy.db.elephantsql.com:5432/nwritrny" 79 | fileprivate let apiURL = "http://api.rutetiderframework.com" 80 | 81 | @IBAction func subscribeAction(_ sender: Any) { 82 | let headers = ["content-type": "application/x-www-form-urlencoded"] 83 | 84 | let postData = NSMutableData(data: "url=\(databaseURL)".data(using: .utf8)!) 85 | postData.append("&user_id=1251252".data(using: .utf8)!) 86 | postData.append("&group_name=PD-3431".data(using: .utf8)!) 87 | 88 | let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/subscribers/add_subscriber")! as URL, 89 | cachePolicy: .useProtocolCachePolicy, 90 | timeoutInterval: 10.0) 91 | request.httpMethod = "PUT" 92 | request.allHTTPHeaderFields = headers 93 | request.httpBody = postData as Data 94 | 95 | let session = URLSession.shared 96 | let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in 97 | if (error != nil) { 98 | print(error) 99 | } else { 100 | let httpResponse = response as? HTTPURLResponse 101 | print(httpResponse) 102 | } 103 | }) 104 | 105 | dataTask.resume() 106 | } 107 | 108 | @IBAction func getSubscriptionInfoAction(_ sender: Any) { 109 | 110 | let headers = ["content-type": "application/x-www-form-urlencoded"] 111 | 112 | let postData = NSMutableData(data: "url=\(databaseURL)".data(using: .utf8)!) 113 | postData.append("&user_id=1251252".data(using: String.Encoding.utf8)!) 114 | 115 | let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/subscribers/get_subscriber_group")! as URL, 116 | cachePolicy: .useProtocolCachePolicy, 117 | timeoutInterval: 10.0) 118 | request.httpMethod = "POST" 119 | request.allHTTPHeaderFields = headers 120 | request.httpBody = postData as Data 121 | 122 | let session = URLSession.shared 123 | let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in 124 | if (error != nil) { 125 | print(error) 126 | } else if let jsonData = data { 127 | do { 128 | let json = try JSONSerialization.jsonObject(with: jsonData) as? Dictionary 129 | print(json?["group"]) 130 | } catch let error{ 131 | print(error) 132 | } 133 | } 134 | }) 135 | 136 | dataTask.resume() 137 | } 138 | 139 | } 140 | ``` 141 | 142 | ## **Current dates** 143 | 144 | С помощью данного компонента вы можете управлять датами, которые вам понадобятся при отображении расписания. 145 | 146 | ```python 147 | import requests 148 | import json 149 | 150 | api_url = 'http://api.rutetiderframework.com' 151 | 152 | database_url = 'postgres://nwritrny:VQJnfVmooh3S0TkAghEgA--YOxoaPJOR@stampy.db.elephantsql.com:5432/nwritrny' 153 | # Это тестовый параметр, в запросе должна быть ссылка на вашу рабочую базу данных 154 | 155 | r = requests.post(api_url + '/currentdates/', data=json.dumps({ 156 | 'url': database_url}), headers={'content-type': 'application/json'}) 157 | 158 | print(r.status_code) 159 | # 200 160 | # Если вы работаете с компонентом впервые, вам необходимо проинициализировать необходимые таблицы, 161 | # то есть вызвать соответсвующий метод. 162 | 163 | r = requests.put('http://api.rutetiderframework.com/currentdates/add_current_dates', data=json.dumps({ 164 | 'url': database_url, 165 | 'today': '07.04', 166 | 'tomorrow': '08.04'}), headers={'content-type': 'application/json'}) 167 | 168 | r = requests.post('http://api.rutetiderframework.com/currentdates/get_current_dates', data=json.dumps({ 169 | 'url': database_url}), headers={'content-type': 'application/json'}) 170 | 171 | print(r.json()) 172 | # {'dates': ['07.04', '08.04']} 173 | 174 | ``` 175 | ## **User position** 176 | 177 | Важным компонентом является получение текущего состояния пользователя, которое позволит грамотно и быстро отобразить следующее состояние или обратное, если пользователь захотел вернуться. Например, если пользователь выбирает группу, то нам необходимо знать, какий выбор пользователь уже сделал (факультет и курс), а если он ошибься курсом — то среагировать на нажатие кнопки «Вернуться назад» ([примеры кода взяты из работающего авторского примера](https://github.com/DmytryiStriletskyi/DuttyBot)). 178 | 179 | ```python 180 | # Ловим нажатие кнопки пользователем 181 | @bot.message_handler(func=lambda mess: '1 курс' == mess.text or '2 курс' == mess.text or 182 | '3 курс' == mess.text or '4 курс' == mess.text or '5 курс' == mess.text or 183 | '6 курс' == mess.text or '7 курс' == mess.text, content_types=['text']) 184 | def handle_text(message): 185 | UserPosition(database_url).set_course_position(str(message.chat.id), message.text[:1]) 186 | # Записывааем в специальный метод «set_course_position» идентификатор пользователя (message.chat.id) и 187 | # и собственно выбор (message.text[:1] - '7 курс'[:1] = '7') 188 | faculty, course = UserPosition(database_url).get_faculty_and_course(str(message.chat.id)) 189 | # «get_faculty_and_course» помогает узнать сделанный выбор пользователя ранее 190 | groups_list = Timetable(database_url).get_all_groups(faculty, course) 191 | # По факультету и курсу получаем список групп и сортируем его 192 | groups_list.sort() 193 | keyboard.group_list_by_faculty_and_group(groups_list, message) 194 | # Выводим на экран (это встроенный метод другой библиотеки, частный случай) 195 | ``` 196 | 197 | «Вернуться назад» в авторском проекте выглядит так. 198 | 199 | ![back_button](https://habrastorage.org/files/d4a/1da/0ac/d4a1da0acc364c8a88799c490075d90d.png) 200 | 201 | Возвращение на одно меню назад реализовывается немного сложнее, поэтому давайте разберем следующее. 202 | 203 | ![user_position](https://habrastorage.org/files/8a6/9ae/4f0/8a69ae4f026245f3b77b64f0a04ca292.png) 204 | 205 | Чтобы знать, какое меню необходимо пользовалю, если он хочет вернуться назад, нам нужно воспользоваться методом 206 | «back_keyboard», который подскажет на какой позиции остановился пользователь. Из схемы видно, что позиция равна единице (1) — цифре, означающей порядковый номер меню, на котором пользователь «застрял», значит, вернуться надо на индексовую позицию ноль (1 - 1). И еще раз: индекс — какое меня было до предпоследним, позиия пользователя — какое меню сейчас. Как вы отображаете меню и где вы его храните — дело вашего приложения, но получение позиции уже работа фреймворка. 207 | 208 | ```python 209 | @bot.message_handler(func=lambda mess: 'Вернуться назад' == mess.text, content_types=['text']) 210 | def handle_text(message): 211 | user_position = UserPosition(database_url).back_keyboard(str(message.chat.id)) 212 | if user_position == 1: 213 | UserPosition(database_url).cancel_getting_started(str(message.chat.id)) 214 | keyboard.main_menu(message) 215 | 216 | if user_position == 2: 217 | UserPosition(database_url).cancel_faculty(str(message.chat.id)) 218 | keyboard.get_all_faculties(message) 219 | 220 | if user_position == 3: 221 | UserPosition(database_url).cancel_course(str(message.chat.id)) 222 | faculty = UserPosition(database_url).verification(str(message.chat.id)) 223 | if faculty != "Загальні підрозділи" and faculty != 'Заочне навчання': 224 | keyboard.stable_six_courses(message) 225 | 226 | if faculty == "Загальні підрозділи": 227 | keyboard.stable_one_course(message) 228 | 229 | if faculty == "Заочне навчання": 230 | keyboard.stable_three_courses(message) 231 | 232 | if user_position == 4: 233 | UserPosition(database_url).cancel_group(str(message.chat.id)) 234 | faculty, course = UserPosition(database_url).get_faculty_and_course(str(message.chat.id)) 235 | groups_list = Timetable(database_url).get_all_groups(faculty, course) 236 | groups_list.sort() 237 | keyboard.group_list_by_faculty_and_group(groups_list, message) 238 | ``` 239 | 240 | То есть при каждом выборе меню вам надо задавать расположение группы, при желании вернуться - отменять расположение и отображать новое меню согласно индексу. Трудно по-началу понять, но другого выбора нет, если вам не доступны какие-то локальные хранилища как телефон пользователя напрямую (IOS, Android). 241 | 242 | ## **Statistics** 243 | 244 | Вы можете легко вести детальную статистику вашего приложения, например, записывать количество выбранного пользователями факультета, а потом с легкостью получать данную цифру и отображать в какую-нибудь админ-панель. 245 | 246 | ```swift 247 | func initializeDatabase() { 248 | let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/statistics/")! as URL, 249 | cachePolicy: .useProtocolCachePolicy, 250 | timeoutInterval: 10.0) 251 | request.httpMethod = "POST" 252 | request.allHTTPHeaderFields = headers 253 | 254 | let session = URLSession.shared 255 | let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) 256 | 257 | dataTask.resume() 258 | } 259 | 260 | func addStatistic() { 261 | 262 | let body = ["url": databaseURL, "user_id": "1251252", "point": "faculty", "date": "06.04.2017"] 263 | 264 | var jsonBody: Data? 265 | 266 | do { 267 | jsonBody = try JSONSerialization.data(withJSONObject: body) 268 | } catch { 269 | } 270 | 271 | let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/statistics/add_statistics")! as URL, 272 | cachePolicy: .useProtocolCachePolicy, 273 | timeoutInterval: 10.0) 274 | request.httpMethod = "PUT" 275 | request.allHTTPHeaderFields = headers 276 | request.httpBody = jsonBody 277 | 278 | let session = URLSession.shared 279 | let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) 280 | 281 | dataTask.resume() 282 | } 283 | 284 | func getStatistic() { 285 | let body = ["url": databaseURL, "user_id": "1251252"] 286 | var jsonBody: Data? 287 | do { 288 | jsonBody = try JSONSerialization.data(withJSONObject: body) 289 | } catch { 290 | } 291 | let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/statistics/get_statistics_general")! as URL, 292 | cachePolicy: .useProtocolCachePolicy, 293 | timeoutInterval: 10.0) 294 | request.httpMethod = "POST" 295 | request.allHTTPHeaderFields = headers 296 | request.httpBody = jsonBody 297 | 298 | let session = URLSession.shared 299 | let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) 300 | dataTask.resume() 301 | } 302 | 303 | func callback(_ data: Data?, _ resp: URLResponse?, _ error: Error?) { 304 | printResponse(resp, error: error) 305 | parseResponse(data) 306 | } 307 | 308 | func parseResponse(_ data: Data?) { 309 | if let jsonData = data { 310 | do { 311 | let json = try JSONSerialization.jsonObject(with: jsonData) as? Dictionary 312 | print(json ?? "json is nil") 313 | } catch let error{ 314 | print(error) 315 | } 316 | } 317 | } 318 | 319 | func printResponse(_ response: URLResponse?, error: Error?) { 320 | if (error != nil) { 321 | print(error!) 322 | } else { 323 | let httpResponse = response as? HTTPURLResponse 324 | print(httpResponse ?? "response is nil") 325 | } 326 | } 327 | ``` 328 | # **Rutetider API** 329 | 330 | В зависимости от выбора технологий, языка программирования, сервера и прочих параметров, вам будет ноебходимо выбрать для себя — с каким интерфейсом вы будете работать, они отличаются в зависимости от направления. 331 | Примеры кода вы уже видели [здесь](https://github.com/DmytryiStriletskyi/Rutetider/wiki/%D0%9A%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82%D1%8B-%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B). 332 | 333 | 334 | 335 | ## **Python** 336 | 337 | Данный путь подойдет разработчикам, которые имеют возможность работать с Python-библиотекой напрямую, например, 338 | Telegram-Bot. Библиотека доступна по ссылке - [«Rutetider API wrapper»](https://pypi.python.org/pypi). 339 | 340 | ```python 341 | from rutetider import Timetable 342 | 343 | timetable = Timetable(database_url) 344 | timetable.add_lesson('IT', '3', 'PD-31', '18.10', 'Программирование', 345 | '451', '2', 'Шевченко Т.Г.') 346 | # params: faculty, course, group_name, lesson_date, lesson_title, 347 | # lesson_classroom, lesson_order, lesson_teacher 348 | ``` 349 | 350 | ## **REST-API** 351 | 352 | Пример на Swift. 353 | ```swift 354 | func addLesson() { 355 | 356 | let body = ["url": databaseURL, "faculty": "IT", "course": "3", "group_name": "PD-31", 357 | "lesson_date": "18.10", "lesson_title": "Программирование", "lesson_classroom": "451", 358 | "lesson_order": "2", "lesson_teacher": "Шевченко Т.Г."] 359 | 360 | var jsonBody: Data? 361 | 362 | do { 363 | jsonBody = try JSONSerialization.data(withJSONObject: body) 364 | } catch { 365 | } 366 | 367 | let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/timetable/add_lesson")! as URL, 368 | cachePolicy: .useProtocolCachePolicy, 369 | timeoutInterval: 10.0) 370 | request.httpMethod = "PUT" 371 | request.allHTTPHeaderFields = headers 372 | request.httpBody = jsonBody 373 | 374 | let session = URLSession.shared 375 | let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) 376 | 377 | dataTask.resume() 378 | } 379 | ``` 380 | 381 | Пример на Python. 382 | ```python 383 | 384 | r = requests.put('http://api.rutetiderframework.com/timetable/add_lesson', data=json.dumps({ 385 | 'url': database_url, 'faculty': 'IT', 'course': '3', 'group_name': 'PD-31, 386 | 'lesson_date': '18.10', 'lesson_title': 'Программирование', 387 | 'lesson_classroom': '451', 'lesson_order': '2', 'lesson_teacher': 'Шевченко Т.Г.'}), 388 | headers={'content-type': 'application/json'}) 389 | ``` 390 | 391 | Вы можете обратить внимание, что отличий в методах почти нет, за исключением подхода программирования, то есть название функции и аргументов через библиотеку соотвествует пути и параметрам в запросе. 392 | 393 | ## **Правила использования фреймворка** 394 | 395 | 1. Запрос к REST-API всегда требует параметр «database_url», работа через Python-библиотеку — только при инициализации объекта. 396 | 2. Ответ на метод всегда в формате **JSON**. 397 | 3. Обращаться к АПИ необходимо по заданому пути: 398 | ``` 399 | http://api.rutetiderframework.com 400 | ``` 401 | 402 | То есть, если вы хотите использовать метод «add_lesson», в документации сказано, что к которому надо прописывать «/timetable/add_lesson», у вас должно получиться следующее. 403 | ``` 404 | http://api.rutetiderframework.com/timetable/add_lesson 405 | ``` 406 | 4. Вы можете заносить даты (и время) только в строковом формате (ничего не мешает вам заносить строковый объект формата даты, а потом конвертировать), но давайте согласуемся, что отмечать только дату будем в таком виде — "26.04.2017" (день.месяц.год). 407 | 408 | # **Базы данных для разработки** 409 | 410 | Параметр «database_url» необходим, чтобы фреймворк куда-то записывал данные и откуда-то их брал, едиснтвенным верным подходом в решении это задачи явялется регистрация своего аккаунта на каком-нибудь ресурсе, который предоставляет бесплатные инстансы под хранение данных. 411 | 412 | ## **Elephant SQL** 413 | 414 | Адрес — https://www.elephantsql.com 415 | 416 | Регистрация — https://customer.elephantsql.com/login (можно через Google или Github) 417 | 418 | ![Register](https://habrastorage.org/files/015/ea1/bcf/015ea1bcfc9141e4abe06987a572290f.png) 419 | ![Register](https://habrastorage.org/files/dc7/0ae/548/dc70ae5485f54af09c67ea508bbb7453.png) 420 | ![Details](https://habrastorage.org/files/9e4/f0c/248/9e4f0c24826a41e69e93570b2436f5b6.png) 421 | 422 | Необходимый параметр в виде строки — URL. 423 | ```python 424 | database_url = 'postgres://nwritrny:VQJnfVmooh3S0TkAghEgA--YOxoaPJOR@stampy.db.elephantsql.com:5432/nwritrny' 425 | ``` 426 | 427 | Интерфейс приятный и выглядит так. 428 | 429 | ![Console](https://habrastorage.org/files/b77/173/583/b77173583e4b474aa31177597f8ff434.png) 430 | 431 | # API 432 | 433 | Перед использование API, пожалуйста, ознакомьтесь с [правилами](https://github.com/DmytryiStriletskyi/Rutetider/wiki/Rutetider-API#%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D1%84%D1%80%D0%B5%D0%B9%D0%BC%D0%B2%D0%BE%D1%80%D0%BA%D0%B0). 434 | 435 | Ниже представлены таблицы, разделенные на четыре колонки, первая пара из которых относится к REST-API (метод и путь), вторая к Python-библиотеке (класс и метод класса). 436 | 437 | ## **create_timetable** 438 | 439 | Используйте этот метод для инициализации таблици для расписания, использовать данный метод в Python-библиотеке не обязательно, при создании объекта класса таблица создается автоматически. 440 | 441 | method| url | class | method 442 | -------- | ----------------- | -------- | ----------------- 443 | POST | timetable/ | Timetable | create_timetable() 444 | 445 | **Parameters:** database_url 446 | 447 | *** 448 | 449 | ## **clear_timetable** 450 | 451 | Используйте данный метод для полного очищения таблицы. 452 | 453 | method| url | class | method 454 | -------- | ----------------- | -------- | ----------------- 455 | DELETE | timetable/clear_timetable | Timetable | clear_timetable() 456 | 457 | **Parameters:** database_url 458 | 459 | *** 460 | 461 | ## **add_lesson** 462 | 463 | Используйте данный метод для добавления данных об одном уроке. 464 | 465 | method| url | class | method 466 | -------- | ----------------- | -------- | ----------------- 467 | PUT | timetable/add_lesson | Timetable | add_lesson() 468 | 469 | **Parameters:** database_url, faculty, course, group_name, lesson_date, lesson_title, lesson_classroom, lesson_order, lesson_teacher 470 | 471 | *** 472 | 473 | ## **get_lesson** 474 | 475 | Используйте данный метод для получения данных об уроках для определенной группы и дня. 476 | 477 | method| url | class | method 478 | -------- | ----------------- | -------- | ----------------- 479 | POST | timetable/get_lesson | Timetable | get_lesson() 480 | 481 | **Parameters:** database_url, group_name, lesson_date 482 | 483 | *** 484 | 485 | ## **get_all_courses** 486 | 487 | Используйте данный метод для получения списка курсов соотвественно указанному факультету. 488 | 489 | method| url | class | method 490 | -------- | ----------------- | -------- | ----------------- 491 | POST | timetable/get_all_courses | Timetable | get_all_courses() 492 | 493 | **Parameters:** database_url, faculty 494 | 495 | *** 496 | 497 | ## **get_all_groups** 498 | 499 | Используйте данный метод для получения списка курсов соотвественно указанному курсу и факультету. 500 | 501 | method| url | class | method 502 | -------- | ----------------- | -------- | ----------------- 503 | POST | timetable/get_all_courses | Timetable | get_all_groups() 504 | 505 | **Parameters:** database_url, faculty, course 506 | 507 | *** 508 | 509 | ## **create_subscribers** 510 | 511 | Используйте этот метод для инициализации таблици для подписчиков, использовать данный метод в Python-библиотеке не обязательно, при создании объекта класса таблица создается автоматически. 512 | 513 | method| url | class | method 514 | -------- | ----------------- | -------- | ----------------- 515 | POST | subscribers/ | Subscribers | create_timetable() 516 | 517 | **Parameters:** database_url 518 | 519 | *** 520 | 521 | ## **clear_subscribers** 522 | 523 | Используйте данный метод для полного очищения таблицы. 524 | 525 | method| url | class | method 526 | -------- | ----------------- | -------- | ----------------- 527 | DELETE | subscribers/clear_subscribers | Subscribers | clear_subscribers() 528 | 529 | **Parameters:** database_url 530 | 531 | *** 532 | 533 | ## **add_subscriber** 534 | 535 | Используйте данный метод для добавления подписчика. 536 | 537 | method| url | class | method 538 | -------- | ----------------- | -------- | ----------------- 539 | PUT | subscribers/add_subscriber | Subscribers | add_subscriber() 540 | 541 | **Parameters:** database_url, user_id, group_name 542 | 543 | *** 544 | 545 | ## **get_subscriber_group** 546 | 547 | Используйте данный метод для получения группы, на которую подписался пользователь. 548 | 549 | method| url | class | method 550 | -------- | ----------------- | -------- | ----------------- 551 | POST | subscribers/get_subscriber_group | Subscribers | get_subscriber_group() 552 | 553 | **Parameters:** database_url, user_id 554 | 555 | *** 556 | 557 | ## **is_subscriber** 558 | 559 | Используйте данный метод для проверки, является ли пользователь подписчиком. 560 | 561 | method| url | class | method 562 | -------- | ----------------- | -------- | ----------------- 563 | POST | subscribers/is_subscriber | Subscribers | is_subscriber() 564 | 565 | **Parameters:** database_url, user_id 566 | 567 | *** 568 | 569 | ## **unsubscribe** 570 | 571 | Используйте данный метод для отмени подписки определенному пользователю. 572 | 573 | method| url | class | method 574 | -------- | ----------------- | -------- | ----------------- 575 | DELETE | subscribers/unsubscribe | Subscribers | unsubscribe() 576 | 577 | **Parameters:** database_url, user_id 578 | 579 | *** 580 | 581 | 582 | ## **create_current_dates** 583 | 584 | Используйте этот метод для инициализации таблици для дат, использовать данный метод в Python-библиотеке не обязательно, при создании объекта класса таблица создается автоматически. 585 | 586 | method| url | class | method 587 | -------- | ----------------- | -------- | ----------------- 588 | POST | currentdates/ | CurrentDates | create_current_dates() 589 | 590 | **Parameters:** database_url 591 | 592 | *** 593 | 594 | ## **clear_current_dates** 595 | 596 | Используйте данный метод для полного очищения таблицы. 597 | 598 | method| url | class | method 599 | -------- | ----------------- | -------- | ----------------- 600 | DELETE | currentdates/clear_current_dates | CurrentDates | clear_current_dates() 601 | 602 | **Parameters:** database_url 603 | 604 | *** 605 | 606 | ## **add_current_dates** 607 | 608 | Используйте данный метод для добавления дат на сегодняшний и завтрашний день. 609 | 610 | method| url | class | method 611 | -------- | ----------------- | -------- | ----------------- 612 | PUT | currentdates/add_current_dates | CurrentDates | add_current_dates() 613 | 614 | **Parameters:** database_url, today, tomorrow 615 | 616 | *** 617 | 618 | ## **get_current_dates** 619 | 620 | Используйте данный метод для получения сегодняшнего и завтрашнего дня. 621 | 622 | method| url | class | method 623 | -------- | ----------------- | -------- | ----------------- 624 | POST | currentdates/get_current_dates | CurrentDates | get_current_dates() 625 | 626 | **Parameters:** database_url 627 | 628 | *** 629 | 630 | 631 | ## **create_userposition** 632 | 633 | Используйте этот метод для инициализации таблици для управления состоянием пользователя, использовать данный метод в Python-библиотеке не обязательно, при создании объекта класса таблица создается автоматически. 634 | 635 | method| url | class | method 636 | -------- | ----------------- | -------- | ----------------- 637 | POST | userposition/ | UserPosition | create_userposition() 638 | 639 | **Parameters:** database_url 640 | 641 | *** 642 | 643 | ## **clear_user_position** 644 | 645 | Используйте данный метод для полного очищения таблицы. 646 | 647 | method| url | class | method 648 | -------- | ----------------- | -------- | ----------------- 649 | DELETE | userposition/clear_user_position | UserPosition | clear_user_position() 650 | 651 | **Parameters:** database_url 652 | 653 | *** 654 | 655 | ## **clear_user_data** 656 | 657 | Используйте данный метод для очищения всех данных о позиции пользователя. 658 | 659 | method| url | class | method 660 | -------- | ----------------- | -------- | ----------------- 661 | DELETE | userposition/clear_user_data | UserPosition | clear_user_data() 662 | 663 | **Parameters:** database_url, user_id 664 | 665 | *** 666 | 667 | ## **set_getting_position** 668 | 669 | Используйте данный метод для добавления позиции, когда пользователь совершил переход с первого меня во второе (нажал на кнопку получения расписания и остановился на выборе факультета). 670 | 671 | method| url | class | method 672 | -------- | ----------------- | -------- | ----------------- 673 | PUT | userposition/set_getting_position | UserPosition | set_getting_position() 674 | 675 | **Parameters:** database_url, user_id 676 | 677 | *** 678 | 679 | ## **set_faculty_position** 680 | 681 | Используйте данный метод для добавления позиции, когда пользователь выбрал факультет и остановился на выборе курса. 682 | 683 | method| url | class | method 684 | -------- | ----------------- | -------- | ----------------- 685 | PUT | userposition/set_faculty_position | UserPosition | set_faculty_position() 686 | 687 | **Parameters:** database_url, user_id, faculty 688 | 689 | *** 690 | 691 | ## **set_course_position** 692 | 693 | Используйте данный метод для добавления позиции, когда пользователь выбрал курс и остановился на выборе группы. 694 | 695 | method| url | class | method 696 | -------- | ----------------- | -------- | ----------------- 697 | PUT | userposition/set_course_position | UserPosition | set_course_position() 698 | 699 | **Parameters:** database_url, user_id, course 700 | 701 | *** 702 | 703 | ## **set_group_position** 704 | 705 | Используйте данный метод для добавления позиции, когда пользователь выбрал группу и остановился на выборе даты расписания и прочих возможных функций. 706 | 707 | method| url | class | method 708 | -------- | ----------------- | -------- | ----------------- 709 | PUT | userposition/set_group_position | UserPosition | set_group_position() 710 | 711 | **Parameters:** database_url, user_id, group_name 712 | 713 | *** 714 | 715 | ## **get_faculty_and_course** 716 | 717 | Используйте данный метод для получения указанного факультета и курса пользователем ранее. 718 | 719 | method| url | class | method 720 | -------- | ----------------- | -------- | ----------------- 721 | POST | userposition/get_faculty_and_course | UserPosition | get_faculty_and_course() 722 | 723 | **Parameters:** database_url, user_id 724 | 725 | *** 726 | 727 | ## **verification** 728 | 729 | Используйте данный метод для получения группы, указанной пользователем (название из-за особенности метода). 730 | 731 | method| url | class | method 732 | -------- | ----------------- | -------- | ----------------- 733 | POST | userposition/verification | UserPosition | verification() 734 | 735 | **Parameters:** database_url, user_id 736 | 737 | *** 738 | 739 | ## **cancel_getting_started** 740 | 741 | Используйте данный метод для отмены позиции пользоватя, когда он перешел к выбору факультета. Фактически вы удаляете все записи в базе данных о выборе и «начинаете с чистого листа». 742 | 743 | method| url | class | method 744 | -------- | ----------------- | -------- | ----------------- 745 | DELETE | userposition/cancel_getting_started | UserPosition | cancel_getting_started() 746 | 747 | **Parameters:** database_url, user_id 748 | 749 | *** 750 | 751 | ## **cancel_faculty** 752 | 753 | Используйте данный метод для отмены позиции пользоватя, когда он перешел к выбору курса, тогда вы собственными инструментами возвращаете его на повторный выбор факультета и отменяете его уже проделанный выбор пользователем. 754 | 755 | method| url | class | method 756 | -------- | ----------------- | -------- | ----------------- 757 | PUT | userposition/cancel_faculty | UserPosition | cancel_faculty() 758 | 759 | **Parameters:** database_url, user_id 760 | 761 | *** 762 | 763 | ## **cancel_course** 764 | 765 | Используйте данный метод для отмены позиции пользоватя, когда он перешел к выбору группы, тогда вы собственными инструментами возвращаете его на повторный выбор курса и отменяете его уже проделанный выбор пользователем. 766 | 767 | method| url | class | method 768 | -------- | ----------------- | -------- | ----------------- 769 | PUT | userposition/cancel_course | UserPosition | cancel_course() 770 | 771 | **Parameters:** database_url, user_id 772 | 773 | *** 774 | 775 | ## **cancel_group** 776 | 777 | Используйте данный метод для отмены позиции пользоватя, когда он перешел к последнему меню, тогда вы собственными инструментами возвращаете его на повторный выбор группы и отменяете его уже проделанный выбор пользователем. 778 | 779 | method| url | class | method 780 | -------- | ----------------- | -------- | ----------------- 781 | PUT | userposition/cancel_group | UserPosition | cancel_group() 782 | 783 | **Parameters:** database_url, user_id 784 | 785 | *** 786 | 787 | ## **back_keyboard** 788 | 789 | Используйте данный метод для получение индекса и позиции меню при нажатии кнопки возврата к предыдущему выбору. [По ссыке](https://github.com/DmytryiStriletskyi/Rutetider/wiki/%D0%9A%D0%BE%D0%BC%D0%BF%D0%BE%D0%BD%D0%B5%D0%BD%D1%82%D1%8B-%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D1%8B#user-position) детальное пояснение метода. 790 | 791 | method| url | class | method 792 | -------- | ----------------- | -------- | ----------------- 793 | PUT | userposition/back_keyboard | UserPosition | back_keyboard() 794 | 795 | **Parameters:** database_url, user_id 796 | 797 | *** 798 | 799 | Параметр «point», который вы встретите в дальнейшем, это произвольное текстовое значение, 800 | передавать на месте которого необходимо какое-либо действие. Например, если вы хотите, чтобы 801 | велась статистика по факультетам, то используйте метод добавления статистики и передавайте на место 802 | этой переменной что-то вроде 'faculty', в итоге по этому же значению вы эту статистику и получите. 803 | 804 | ## **create_statistics** 805 | 806 | Используйте этот метод для инициализации таблици для статистики, использовать данный метод в Python-библиотеке не обязательно, при создании объекта класса таблица создается автоматически. 807 | 808 | method| url | class | method 809 | -------- | ----------------- | -------- | ----------------- 810 | POST | statistics/ | Statistics | create_statistics() 811 | 812 | **Parameters:** database_url 813 | 814 | *** 815 | 816 | ## **clear_statistics** 817 | 818 | Используйте данный метод для полного очищения таблицы. 819 | 820 | method| url | class | method 821 | -------- | ----------------- | -------- | ----------------- 822 | DELETE | statistics/clear_statistics | Statistics | clear_statistics() 823 | 824 | **Parameters:** database_url 825 | 826 | *** 827 | 828 | ## **add_statistics** 829 | 830 | Используйте данный метод для добавления статистики. 831 | 832 | method| url | class | method 833 | -------- | ----------------- | -------- | ----------------- 834 | PUT | statistics/add_statistics | Statistics | add_statistics() 835 | 836 | **Parameters:** database_url, user_id, point, date 837 | 838 | *** 839 | 840 | ## **get_statistics_general** 841 | 842 | Используйте данный метод для получения общей статистики вашего приложения. 843 | 844 | method| url | class | method 845 | -------- | ----------------- | -------- | ----------------- 846 | POST | statistics/get_statistics_general | Statistics | get_statistics_general() 847 | 848 | **Parameters:** database_url 849 | 850 | *** 851 | 852 | ## **get_statistics_counts** 853 | 854 | Используйте данный метод для получения количества записанных вами point`ов в соответстии к каждому типу (например, для факультета, курса и подписки). 855 | 856 | method| url | class | method 857 | -------- | ----------------- | -------- | ----------------- 858 | POST | statistics/get_statistics_counts | Statistics | get_statistics_counts() 859 | 860 | **Parameters:** database_url 861 | 862 | *** 863 | 864 | ## **get_statistics_between_dates** 865 | 866 | Используйте данный метод для получения статистики за определенный период (от и до какого-то дня). 867 | 868 | method| url | class | method 869 | -------- | ----------------- | -------- | ----------------- 870 | POST | statistics/get_statistics_between_dates | Statistics | get_statistics_between_dates() 871 | 872 | **Parameters:** database_url, date_from, date_from 873 | 874 | *** 875 | 876 | ## **get_statistics_by_point** 877 | 878 | Используйте данный метод для получения статистики по определенному point`у. 879 | 880 | method| url | class | method 881 | -------- | ----------------- | -------- | ----------------- 882 | POST | statistics/get_statistics_by_point | Statistics | get_statistics_by_point() 883 | 884 | **Parameters:** database_url, point 885 | 886 | *** 887 | 888 | ## **point_between_dates** 889 | 890 | Используйте данный метод для получения статистики по определенному point`у в определенный период (от и до какого-то дня). 891 | 892 | method| url | class | method 893 | -------- | ----------------- | -------- | ----------------- 894 | POST | statistics/point_between_dates | Statistics | point_between_dates() 895 | 896 | **Parameters:** database_url, point, date_from, date_to 897 | 898 | *** 899 | --------------------------------------------------------------------------------