こんにちは、Habrahabr! iOS 、 Android 、 Telegram-botなどのモバイルデバイス向けの既成のアーキテクチャソリューション、および記事の著者のペットプロジェクトとして機能するhttp-requestsの処理をサポートするプラットフォームは、大学や学校に「ポケット」クラススケジュールを実装したい人にとって興味深いものになります。
出版の内容:
- フレームワークの作成に先行したもの。
- 「Rutetider」で解決されるプログラマーの問題。
- 楽器の建築構造の詳細。
- メインフレームワークであるコンポーネント、開発を改善するモジュール、およびさまざまな例について。
はじめに
オープンソースコミュニティに貢献するために、大部分およびそれよりも少ない範囲で、モバイルデバイスでの大学の時刻表へのアクセス不能の問題(実際、アクセシビリティ、しかし非常に非適応的で「長い」)を解決するために、私は最高の機会を使わなければなりませんでした-テレグラムボット(興味深い場合-Habrahabrに関する記事)を作成し、大学だけでなく問題を解決するために-小さなフレームワーク。
フレームワークは、ボットと同じツールを使用して、最初のソリューションに基づいて決定されましたが、モバイルアプリケーションの整合性を直接サポートするプラットフォーム(iOS、Android、およびその他のプラットフォーム(webたとえば、電話用のアダプティブレイアウトを備えたアプリケーション)。
簡単に言えば、2つのタイプの機能へのアクセスが定義されました-REST APIとPythonを直接使用するプログラマーのためのPythonライブラリです。
また、Rutetider
これは、テンプレートシーケンスに基づいた一連のメソッドとツールであり、柔軟ではないかもしれませんが、確かに機能するアプリケーションを作成できます。 まず第一に、それは「ここと今」の決定です。 主な目標が開発の場合-フレームワークを使用せずに、すべてをゼロから作成します。
もう1つの利点は、利用可能なドキュメントであり、作業の説明だけでなく、イラストと指示も含まれているため、理解と開発が大幅に促進されます。
フレームワークのアーキテクチャ
基本原則
上記のように、一般に多くのプログラミング経験がなくても、正確で美しい構造を決定することは非常に難しいので、テンプレートにぶつかる必要がありましたが、その利点は非常に明白で機能していました。
ユーザーに代わって話すと、彼は多くの画面を通過する必要があります:可能な他の多くの中からメインオプション( スケジュールを取得する機能)、 faculty 、 course 、そしてグループと日付自体 (他の多くの有用な機能)を選択します。
図からわかるように、プログラマー自身がユーザーの位置を「キャッチ」し、必要なメニューをさまざまなコンテンツで表示し、そのような条件が存在する場合は統計を保持する必要があります。
必要な方法の詳細をご覧ください。
コンテキストから外れないようにするために、友人と続けましょう-「戻る」ボタン自体はどこに戻るかわからないため、ローカルストレージ(ユーザーの電話など)を使用する可能性なしにプラットフォーム上のユーザーの位置を記録する必要があります同じ位置に「送り」ます。 別の例は、後で学部およびコースごとにグループを決定し、グループごとに対応する日付のスケジュールを選択するために、学生が入力するデータの種類を知ることです。
さらに、プログラマーは、今日と明日の日付に関する便利な作業を当てにすることができます。つまり、正確で関連する値を入力する機会と、受け取る機会があります。
データの入力を停止しましたが、フレームワークには、聴衆と時間から教師のデータまで、大学のペアに関する情報をさらに構成するのに役立つメソッドが用意されていることに注意してください。
講義オプションを追加する例を保持します。
from rutetider import Timetable timetable = Timetable(database_url) timetable.add_lesson('IT', '3', 'PD-31', '18.10', '', '451', '2', ' ..') # params: faculty, course, group_name, lesson_date, lesson_title, # lesson_classroom, lesson_order, lesson_teacher
私はまだそれがどのように機能するか理解していない
一部のプラットフォームが不要な機能を使用しないように、ツールに少しモジュール性を追加しようとしましたが、逆に、Rutetiderを使用したいすべての人に手錠をかけました-サーバー(ほとんどの場合)とデータベースの存在。
データベースを作成する必要があるのは、作成者が自分のスケジュールやその他の貴重な情報のための空きスペースを全員に提供するための十分なリソースを持たないためです。そのため、プログラマーは独自のPostgreSQLを作成し、アクセスへのリンクを共有する必要があります(幸いなことに、それらの1つについて、多くの無料機能がありますI ここで言ってください )。
しかし、誰かがサーバーを検索する必要はないかもしれませんが、大学が毎日または毎週スケジュールを更新する人には確かに必要です-この場合、パーサーによるスケジュール作成ツール、CSVの読み取りまたは便利な方法が必要です。
また、情報技術協会が開発者をサポートしているため、私たちは皆非常に幸運です: Heroku Cloud Platform for Python、Java、Node.js、 Firebase 、 Parse 、 Polljoy -iOS(著者はほとんどの提案を使用しませんでした;追加またはコメントがある場合)アカウント-通知)。
どの機能を頼りにできるか
講義とペア -クラスの処理を担当する全体的な構造のコンポーネント。 ペアを追加した例をご覧になった場合は、領収書をご覧ください。
schedule = timetable.get_lessons('PD-31', '18.10') # params: group_name, lesson_date print(schedule) # {'lessons': { # '3': {'lesson_teacher': ' ..', 'lesson_classroom': # '451', 'lesson_order': '3', 'lesson_title': ''}, # '1': {'lesson_teacher': ' ..', 'lesson_classroom': '118', # 'lesson_order': '1', 'lesson_title': #''}, # '2': {'lesson_teacher': ' ..', 'lesson_classroom': '200', # 'lesson_order': '2', 'lesson_title': #' '}}}
サブスクリプション 。ただし、通知用ではありません。これは、フレームワークの関連性を備えた将来の便利な機能であることがわかりますが、ワンクリックでスケジュールを受け取ることができます。
このアーキテクチャでは、いくつかのボタンを押して、目の前にある多くの画面を見て何かを選択する必要があるため、この機能は非常に便利です。ユーザーは特定のグループに一度登録する必要があり、「スチームバス」はもう必要ありません。
迅速なコード
import UIKit class ViewController: UIViewController { fileprivate let databaseURL = "postgres://nwritrny:VQJnfVmooh3S0TkAghEgA--YOxoaPJOR@stampy.db.elephantsql.com:5432/nwritrny" fileprivate let apiURL = "http://api.rutetiderframework.com" @IBAction func subscribeAction(_ sender: Any) { let headers = ["content-type": "application/x-www-form-urlencoded"] let postData = NSMutableData(data: "url=\(databaseURL)".data(using: .utf8)!) postData.append("&user_id=1251252".data(using: .utf8)!) postData.append("&group_name=PD-3431".data(using: .utf8)!) let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/subscribers/add_subscriber")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PUT" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() } @IBAction func getSubscriptionInfoAction(_ sender: Any) { let headers = ["content-type": "application/x-www-form-urlencoded"] let postData = NSMutableData(data: "url=\(databaseURL)".data(using: .utf8)!) postData.append("&user_id=1251252".data(using: String.Encoding.utf8)!) let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/subscribers/get_subscriber_group")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error) } else if let jsonData = data { do { let json = try JSONSerialization.jsonObject(with: jsonData) as? Dictionary<String, Any> print(json?["group"]) } catch let error{ print(error) } } }) dataTask.resume() } }
今日と明日のスケジュールを作成および受信できる現在の日付 。
import requests import json api_url = 'http://api.rutetiderframework.com' database_url = 'postgres://nwritrny:VQJnfVmooh3S0TkAghEgA--YOxoaPJOR@stampy.db.elephantsql.com:5432/nwritrny' # , r = requests.post(api_url + '/currentdates/', data=json.dumps({ 'url': database_url}), headers={'content-type': 'application/json'}) print(r.status_code) # 200 # , , # . r = requests.put('http://api.rutetiderframework.com/currentdates/add_current_dates', data=json.dumps({ 'url': database_url, 'today': '07.04', 'tomorrow': '08.04'}), headers={'content-type': 'application/json'}) r = requests.post('http://api.rutetiderframework.com/currentdates/get_current_dates', data=json.dumps({ 'url': database_url}), headers={'content-type': 'application/json'}) print(r.json()) # {'dates': ['07.04', '08.04']}
重要なことですが、最初の理解としてはそれほど難しくありませんが、ポイントはユーザーの位置です -組み込みまたはその他の便利な手段を使用することは不可能です。
たとえば、ユーザーがグループを選択した場合、ユーザーがすでに行った選択(学部とコース)を知る必要があり、コースでミスをした場合は、「戻る」ボタンを押して応答します。
@bot.message_handler(func=lambda mess: ' ' == mess.text, content_types=['text']) def handle_text(message): user_position = UserPosition(database_url).back_keyboard(str(message.chat.id)) if user_position == 1: UserPosition(database_url).cancel_getting_started(str(message.chat.id)) keyboard.main_menu(message) if user_position == 2: UserPosition(database_url).cancel_faculty(str(message.chat.id)) keyboard.get_all_faculties(message) if user_position == 3: UserPosition(database_url).cancel_course(str(message.chat.id)) faculty = UserPosition(database_url).verification(str(message.chat.id)) if faculty != "і іі" and faculty != ' ': keyboard.stable_six_courses(message) if faculty == "і іі": keyboard.stable_one_course(message) if faculty == " ": keyboard.stable_three_courses(message) if user_position == 4: UserPosition(database_url).cancel_group(str(message.chat.id)) faculty, course = UserPosition(database_url).get_faculty_and_course(str(message.chat.id)) groups_list = Timetable(database_url).get_all_groups(faculty, course) groups_list.sort() keyboard.group_list_by_faculty_and_group(groups_list, message)
1つのメニューに戻るのはもう少し複雑なので、図で見てみましょう。
ユーザーがどのメニューを必要としているかを知るために、もし彼が戻りたいのであれば、「back_keyboard」メソッドを使用する必要があります。 図から、位置が1(1)-ユーザーが「スタック」しているメニューのシリアル番号を示す数字に等しいことがわかります。これは、インデックス位置ゼロ(1-1 = 0)に戻る必要があることを意味します。 繰り返しますが、インデックス-最後のメニューはどれか、ユーザーの位置-現在はどのメニューか。 メニューの表示方法と保存場所はアプリケーションの仕事ですが、位置を取得することは既にフレームワークの仕事です。
アーキテクチャの最後の部分は統計です。複雑なことはありませんが、多くの有用なことがあります。 たとえば、アプリケーションの詳細な統計を簡単に保持できます。ユーザーが選択した教員の数を記録し、この数を簡単に取得して、管理パネルに表示できます。
迅速なコード
func initializeDatabase() { let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/statistics/")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) dataTask.resume() } func addStatistic() { let body = ["url": databaseURL, "user_id": "1251252", "point": "faculty", "date": "06.04.2017"] var jsonBody: Data? do { jsonBody = try JSONSerialization.data(withJSONObject: body) } catch { } let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/statistics/add_statistics")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PUT" request.allHTTPHeaderFields = headers request.httpBody = jsonBody let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) dataTask.resume() } func getStatistic() { let body = ["url": databaseURL, "user_id": "1251252"] var jsonBody: Data? do { jsonBody = try JSONSerialization.data(withJSONObject: body) } catch { } let request = NSMutableURLRequest(url: NSURL(string: "\(apiURL)/statistics/get_statistics_general")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = jsonBody let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: callback) dataTask.resume() } func callback(_ data: Data?, _ resp: URLResponse?, _ error: Error?) { printResponse(resp, error: error) parseResponse(data) } func parseResponse(_ data: Data?) { if let jsonData = data { do { let json = try JSONSerialization.jsonObject(with: jsonData) as? Dictionary<String, Any> print(json ?? "json is nil") } catch let error{ print(error) } } } func printResponse(_ response: URLResponse?, error: Error?) { if (error != nil) { print(error!) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse ?? "response is nil") } }
ありがとう
完了した作業と思考の流れ全般を説明するための私のアプローチに感謝するだけでなく、より深い関心を示したことを願っています。 そして、あなたが完全に魅了されているなら、私はあなたの質問に答えるか、私の側で開発を手伝ってうれしいです。