├── .gitignore ├── backend ├── backend │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ └── urls.py ├── scripts │ ├── __init__.py │ └── add_user.py ├── users │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── admin.py │ ├── tests.py │ ├── apps.py │ ├── urls.py │ ├── services.py │ ├── serializers.py │ └── views.py ├── distributor │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0040_merge_20200419_1223.py │ │ ├── 0026_merge_20200329_1841.py │ │ ├── 0046_merge_20200420_1908.py │ │ ├── 0024_auto_20200329_1508.py │ │ ├── 0041_auto_20200405_1717.py │ │ ├── 0013_auto_20200327_2023.py │ │ ├── 0056_auto_20200718_1902.py │ │ ├── 0040_auto_20200405_1659.py │ │ ├── 0034_auto_20200402_1233.py │ │ ├── 0039_hospital_hidden.py │ │ ├── 0011_auto_20200326_2123.py │ │ ├── 0023_update_search_fields.py │ │ ├── 0045_auto_20200420_1856.py │ │ ├── 0020_auto_20200328_1131.py │ │ ├── 0048_auto_20200420_1922.py │ │ ├── 0025_auto_20200329_1531.py │ │ ├── 0055_fix_statistics_capacity.py │ │ ├── 0016_auto_20200327_2112.py │ │ ├── 0007_auto_20200325_2322.py │ │ ├── 0018_auto_20200327_2329.py │ │ ├── 0006_auto_20200325_2234.py │ │ ├── 0054_auto_20200426_1123.py │ │ ├── 0036_add_statistic_category.py │ │ ├── 0052_auto_20200422_2007.py │ │ ├── 0050_auto_20200420_1852.py │ │ ├── 0015_add_statistic_categories.py │ │ ├── 0033_add_contact_info.py │ │ ├── 0022_auto_20200329_1031.py │ │ ├── 0043_auto_20200405_1813.py │ │ ├── 0042_auto_20200405_1807.py │ │ ├── 0047_auto_20200420_1920.py │ │ ├── 0005_hospital.py │ │ ├── 0049_auto_20200420_1937.py │ │ ├── 0039_auto_20200418_1754.py │ │ ├── 0002_auto_20200325_1805.py │ │ ├── 0009_auto_20200326_1932.py │ │ ├── 0035_import_hospitals_addresses.py │ │ ├── 0019_auto_20200328_0130.py │ │ ├── 0053_fix_distribution_details.py │ │ ├── 0012_statistic.py │ │ ├── 0021_page.py │ │ ├── 0027_auto_20200330_1631.py │ │ ├── 0039_distribution.py │ │ ├── 0008_auto_20200325_2328.py │ │ ├── 0017_helprequest.py │ │ ├── 0044_auto_20200420_1853.py │ │ ├── 0003_auto_20200325_1806.py │ │ ├── 0014_auto_20200327_2104.py │ │ ├── 0001_initial.py │ │ ├── 0051_add_manager_group.py │ │ └── 0004_donation_donationdetail.py │ ├── templatetags │ │ ├── __init__.py │ │ └── tags.py │ ├── tests.py │ ├── apps.py │ ├── constants.py │ ├── filters.py │ └── translation.py ├── .gitignore ├── static │ ├── css │ │ ├── admin.css │ │ ├── location.css │ │ └── statistic.css │ └── js │ │ └── statistic.js ├── locale │ ├── ky │ │ └── LC_MESSAGES │ │ │ └── django.mo │ └── ru │ │ └── LC_MESSAGES │ │ └── django.mo ├── .env.github ├── .env.dev ├── entrypoint.sh ├── templates │ └── admin │ │ ├── hospital_change_list.html │ │ └── base_site.html ├── Dockerfile ├── common │ └── schemas.py ├── Makefile ├── manage.py ├── requirements.txt ├── README.md └── DEPLOY.md ├── frontend ├── Frontend-Code-Style.xml ├── constants │ ├── helpRequest │ │ └── index.js │ └── messageStatus.js ├── public │ ├── about │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ └── 5.png │ ├── favicon.ico │ ├── mdi_map.png │ ├── tirek_logo.png │ ├── mdi_menu.svg │ ├── x.svg │ ├── info.svg │ ├── mdi_map.svg │ ├── mdi_phone.svg │ ├── mdi_pin_drop.svg │ ├── mdi_contact_phone.svg │ ├── mdi_import_contacts.svg │ ├── mdi_search.svg │ ├── zeit.svg │ └── whatsapp.svg ├── .env.dev ├── styles │ ├── index.scss │ ├── img │ │ ├── mdi_phone.svg │ │ └── mdi_pin_drop.svg │ ├── about.scss │ └── _reset.scss ├── actions │ ├── requests.js │ ├── distributions.js │ ├── regions.js │ ├── districts.js │ ├── donations.js │ ├── hospitals.js │ ├── localities.js │ ├── contacts.js │ └── creators │ │ ├── requests.js │ │ ├── distributions.js │ │ ├── regions.js │ │ ├── donations.js │ │ ├── contacts.js │ │ ├── districts.js │ │ ├── hospitals.js │ │ └── localities.js ├── next.config.js ├── pages │ ├── about.js │ ├── contact.js │ ├── index.js │ ├── donations.js │ ├── _app.js │ └── donations │ │ └── [id].js ├── .gitignore ├── components │ ├── about │ │ ├── BlockHeader.jsx │ │ └── Block.jsx │ ├── layout │ │ ├── Footer.jsx │ │ └── Layout.jsx │ ├── navigation │ │ └── ActiveLink.jsx │ ├── contacts │ │ ├── Info.jsx │ │ └── index.js │ ├── distributions │ │ └── distribution.js │ └── donations │ │ ├── donation.js │ │ └── index.js ├── DEPLOY.md ├── config │ ├── store.js │ ├── rootReducer.js │ ├── loadInitialState.js │ └── with-redux-store.js ├── package.json ├── reducers │ ├── distributionsReducer.js │ ├── donationsReducer.js │ ├── regionsReducer.js │ ├── districtsReducer.js │ ├── hospitalsReducer.js │ ├── localitiesReducer.js │ └── contactsReducer.js ├── utils │ └── ga.js └── README.md ├── mobile ├── assets │ ├── logo.png │ ├── tirek-logo.png │ └── flutter-icon.png ├── ios │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner │ │ ├── AppDelegate.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ ├── main.m │ │ ├── AppDelegate.m │ │ ├── GoogleService-Info.plist │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ └── Main.storyboard │ ├── Runner.xcodeproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── .gitignore │ └── GoogleService-Info.plist ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ └── main │ │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutterlogindemo │ │ │ │ │ └── MainActivity.java │ │ │ │ └── AndroidManifest.xml │ │ ├── google-services.json │ │ └── build.gradle │ ├── .gitignore │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── settings.gradle │ └── build.gradle ├── lib │ ├── models │ │ └── response │ │ │ ├── LogoutResponse.dart │ │ │ ├── NeedsRequestResponse.dart │ │ │ ├── DistributionStatus.dart │ │ │ ├── Measure.dart │ │ │ ├── User.dart │ │ │ ├── AuthenticationResponse.dart │ │ │ ├── DonationResponse.dart │ │ │ ├── NeedsTypeResponse.dart │ │ │ ├── NeedsResponse.dart │ │ │ ├── HospitalResponse.dart │ │ │ └── DistributionResponse.dart │ ├── services │ │ ├── DonationService.dart │ │ ├── AuthenticationService.dart │ │ ├── NeedsService.dart │ │ ├── HospitalService.dart │ │ ├── NeedsTypeService.dart │ │ ├── LogoutService.dart │ │ ├── DistributionsService.dart │ │ ├── NeedsRequestService.dart │ │ └── SharedPreferencesService.dart │ ├── exception │ │ └── TirekException.dart │ ├── helper │ │ └── ApiHelper.dart │ └── pages │ │ └── RootPage.dart ├── .metadata ├── .vscode │ └── launch.json ├── pubspec.yaml ├── README.md ├── test │ └── widget_test.dart ├── tirek_mobile_android.iml └── .gitignore ├── template ├── src │ ├── logo.png │ ├── setupTests.js │ ├── App.test.js │ ├── index.js │ └── img │ │ └── mdi_search.svg ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── .gitignore └── package.json ├── Makefile ├── .github └── workflows │ ├── action.yml │ └── django.yml └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /backend/backend/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/distributor/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/distributor/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/distributor/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | venv 3 | .env 4 | __pycache__ 5 | assets -------------------------------------------------------------------------------- /backend/static/css/admin.css: -------------------------------------------------------------------------------- 1 | input.vIntegerField { 2 | width: 8em; 3 | } -------------------------------------------------------------------------------- /frontend/Frontend-Code-Style.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/static/css/location.css: -------------------------------------------------------------------------------- 1 | #id_location-div-map { 2 | display: grid !important; 3 | } -------------------------------------------------------------------------------- /backend/users/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /frontend/constants/helpRequest/index.js: -------------------------------------------------------------------------------- 1 | export const SUCCESS = 1; 2 | export const ERROR = -1; -------------------------------------------------------------------------------- /backend/users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/users/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/distributor/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /mobile/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/assets/logo.png -------------------------------------------------------------------------------- /template/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/template/src/logo.png -------------------------------------------------------------------------------- /template/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/public/about/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/about/1.png -------------------------------------------------------------------------------- /frontend/public/about/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/about/2.png -------------------------------------------------------------------------------- /frontend/public/about/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/about/3.png -------------------------------------------------------------------------------- /frontend/public/about/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/about/4.png -------------------------------------------------------------------------------- /frontend/public/about/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/about/5.png -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/mdi_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/mdi_map.png -------------------------------------------------------------------------------- /template/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/template/public/favicon.ico -------------------------------------------------------------------------------- /frontend/.env.dev: -------------------------------------------------------------------------------- 1 | API_URL=http://antivirus.el.kg/api/v1 2 | MAP_KEY= 3 | RECAPTCHA_SITE_KEY= 4 | RECAPTCHA_SECRET_KEY= -------------------------------------------------------------------------------- /mobile/assets/tirek-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/assets/tirek-logo.png -------------------------------------------------------------------------------- /frontend/public/tirek_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/frontend/public/tirek_logo.png -------------------------------------------------------------------------------- /mobile/assets/flutter-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/assets/flutter-icon.png -------------------------------------------------------------------------------- /backend/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | name = 'users' 6 | -------------------------------------------------------------------------------- /frontend/constants/messageStatus.js: -------------------------------------------------------------------------------- 1 | export const INIT = 0; 2 | export const SUCCESS = 100; 3 | export const FAILURE = -1; 4 | -------------------------------------------------------------------------------- /backend/locale/ky/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/backend/locale/ky/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /backend/locale/ru/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/backend/locale/ru/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /backend/distributor/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DistributorConfig(AppConfig): 5 | name = 'distributor' 6 | -------------------------------------------------------------------------------- /mobile/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | init: 2 | cp backend/.env.dev backend/.env 3 | 4 | helpmegod: 5 | docker-compose up --build 6 | 7 | thanksgod: 8 | docker-compose down 9 | -------------------------------------------------------------------------------- /mobile/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /mobile/android/gradle.properties: -------------------------------------------------------------------------------- 1 | android.enableJetifier=true 2 | android.useAndroidX=true 3 | org.gradle.jvmargs=-Xmx1536M 4 | 5 | android.enableR8=true 6 | -------------------------------------------------------------------------------- /mobile/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /frontend/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import "reset"; 2 | @import "~bootstrap/dist/css/bootstrap.min.css"; 3 | @import "main"; 4 | @import "about"; 5 | @import "mobile"; 6 | -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /backend/static/css/statistic.css: -------------------------------------------------------------------------------- 1 | .disabled { 2 | display: none; 3 | } 4 | 5 | .model-hospital #content-main ul.object-tools > li { 6 | display: inline-block !important; 7 | } -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /mobile/lib/models/response/LogoutResponse.dart: -------------------------------------------------------------------------------- 1 | class LogoutResponse { 2 | LogoutResponse(); 3 | 4 | factory LogoutResponse.fromJson(dynamic json) { 5 | return LogoutResponse(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /mobile/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /frontend/actions/requests.js: -------------------------------------------------------------------------------- 1 | export const CREATE_REQUEST = 'CREATE_REQUEST'; 2 | export const SUCCESS_CREATE_REQUEST = 'SUCCESS_CREATE_REQUEST'; 3 | export const FAILURE_CREATE_REQUEST = 'FAILURE_CREATE_REQUEST'; 4 | -------------------------------------------------------------------------------- /frontend/public/mdi_menu.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Entea/covid-supply-info/HEAD/mobile/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /mobile/lib/models/response/NeedsRequestResponse.dart: -------------------------------------------------------------------------------- 1 | class NeedsRequestResponse { 2 | NeedsRequestResponse(); 3 | 4 | factory NeedsRequestResponse.fromJson(dynamic json) { 5 | return NeedsRequestResponse(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /frontend/actions/distributions.js: -------------------------------------------------------------------------------- 1 | export const FETCH_DISTRIBUTIONS = 'FETCH_DISTRIBUTIONS'; 2 | export const SUCCESS_FETCH_DISTRIBUTIONS = 'SUCCESS_FETCH_DISTRIBUTIONS'; 3 | export const FAILURE_FETCH_DISTRIBUTIONS = 'FAILURE_FETCH_DISTRIBUTIONS'; 4 | -------------------------------------------------------------------------------- /mobile/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /mobile/lib/models/response/DistributionStatus.dart: -------------------------------------------------------------------------------- 1 | class DistributionStatus { 2 | final Status status; 3 | final String value; 4 | 5 | DistributionStatus(this.status, this.value); 6 | } 7 | 8 | enum Status { ready_to_sent, sent, delivered } 9 | -------------------------------------------------------------------------------- /backend/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | 3 | from users.views import LogoutView 4 | 5 | urlpatterns = [ 6 | path('auth/', include('rest_auth.urls')), 7 | path('logout/', LogoutView.as_view(), name='logout_view') 8 | ] 9 | -------------------------------------------------------------------------------- /mobile/lib/models/response/Measure.dart: -------------------------------------------------------------------------------- 1 | class Measure { 2 | final int id; 3 | final String name; 4 | 5 | Measure(this.id, this.name); 6 | 7 | factory Measure.fromJson(dynamic json) { 8 | return Measure(json['id'] as int, json['name'] as String); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mobile/lib/models/response/User.dart: -------------------------------------------------------------------------------- 1 | class User { 2 | final int id; 3 | final String fullName; 4 | 5 | User(this.id, this.fullName); 6 | 7 | factory User.fromJson(dynamic json) { 8 | return User(json['id'] as int, json['full_name'] as String); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mobile/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /template/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /mobile/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Oct 12 18:40:23 SGT 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip 7 | -------------------------------------------------------------------------------- /frontend/public/x.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/.env.github: -------------------------------------------------------------------------------- 1 | DEBUG=on 2 | SECRET_KEY=some-special-secret-key 3 | DISTRIBUTOR_DB_HOST=127.0.0.1 4 | DISTRIBUTOR_DB_NAME=distributor 5 | DISTRIBUTOR_DB_USER=master 6 | DISTRIBUTOR_DB_PASSWORD=123456 7 | DISTRIBUTOR_DB_PORT=5432 8 | MAP_API_KEY= 9 | REDIS_HOST=127.0.0.1 10 | REDIS_PORT=6379 11 | RECAPTCHA_SECRET_KEY= -------------------------------------------------------------------------------- /mobile/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b 8 | channel: beta 9 | -------------------------------------------------------------------------------- /mobile/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /mobile/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /mobile/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /backend/.env.dev: -------------------------------------------------------------------------------- 1 | DEBUG=on 2 | SECRET_KEY=some-special-secret-key 3 | DISTRIBUTOR_DB_HOST=distributor-db 4 | DISTRIBUTOR_DB_NAME=distributor 5 | DISTRIBUTOR_DB_USER=master 6 | DISTRIBUTOR_DB_PASSWORD=123456 7 | DISTRIBUTOR_DB_PORT=5432 8 | MAP_API_KEY= 9 | REDIS_HOST=redis 10 | REDIS_PORT=6379 11 | RECAPTCHA_SECRET_KEY= 12 | ENABLE_LOGGING=off -------------------------------------------------------------------------------- /template/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /backend/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Waiting for postgres..." 4 | 5 | while ! nc -z $DISTRIBUTOR_DB_HOST $DISTRIBUTOR_DB_PORT; do 6 | sleep 0.1 7 | done 8 | 9 | echo "PostgreSQL started" 10 | 11 | python manage.py migrate 12 | python manage.py collectstatic --no-input 13 | python manage.py runserver 0.0.0.0:8000 14 | 15 | exec "$@" -------------------------------------------------------------------------------- /frontend/next.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const nextConfig = { 4 | publicRuntimeConfig: { 5 | apiUrl: process.env.API_URL, 6 | recaptchaSiteKey: process.env.RECAPTCHA_SITE_KEY, 7 | recaptchaSecretKey: process.env.RECAPTCHA_SECRET_KEY, 8 | gaKey: process.env.GA_KEY, 9 | }, 10 | }; 11 | 12 | module.exports = nextConfig; 13 | -------------------------------------------------------------------------------- /frontend/public/info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/mdi_map.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/actions/regions.js: -------------------------------------------------------------------------------- 1 | export const FETCH_REGIONS = 'FETCH_REGIONS'; 2 | export const SUCCESS_FETCH_REGIONS = 'SUCCESS_FETCH_REGIONS'; 3 | export const FAILURE_FETCH_REGIONS = 'FAILURE_FETCH_REGIONS'; 4 | 5 | export const FETCH_REGION = 'FETCH_REGION'; 6 | export const SUCCESS_FETCH_REGION = 'SUCCESS_FETCH_REGION'; 7 | export const FAILURE_FETCH_REGION = 'FAILURE_FETCH_REGION'; -------------------------------------------------------------------------------- /backend/templates/admin/hospital_change_list.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/change_list.html" %} 2 | {% load i18n %} 3 | {% load static %} 4 | 5 | {% block object-tools-items %} 6 | {{ block.super }} 7 |
  • 8 | {% trans "Update Search Fields" %} 9 |
  • 10 | {% endblock %} -------------------------------------------------------------------------------- /backend/distributor/migrations/0040_merge_20200419_1223.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-19 12:23 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0039_hospital_hidden'), 10 | ('distributor', '0039_auto_20200418_1754'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0026_merge_20200329_1841.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-29 18:41 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0022_auto_20200328_1808'), 10 | ('distributor', '0025_auto_20200329_1531'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0046_merge_20200420_1908.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 19:08 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0045_auto_20200420_1856'), 10 | ('distributor', '0040_merge_20200419_1223'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /template/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /frontend/actions/districts.js: -------------------------------------------------------------------------------- 1 | export const FETCH_DISTRICTS = 'FETCH_DISTRICTS'; 2 | export const SUCCESS_FETCH_DISTRICTS = 'SUCCESS_FETCH_DISTRICTS'; 3 | export const FAILURE_FETCH_DISTRICTS = 'FAILURE_FETCH_DISTRICTS'; 4 | 5 | export const FETCH_DISTRICT = 'FETCH_DISTRICT'; 6 | export const SUCCESS_FETCH_DISTRICT = 'SUCCESS_FETCH_DISTRICT'; 7 | export const FAILURE_FETCH_DISTRICT = 'FAILURE_FETCH_DISTRICT'; 8 | -------------------------------------------------------------------------------- /frontend/actions/donations.js: -------------------------------------------------------------------------------- 1 | export const FETCH_DONATIONS = 'FETCH_DONATIONS'; 2 | export const SUCCESS_FETCH_DONATIONS = 'SUCCESS_FETCH_DONATIONS'; 3 | export const FAILURE_FETCH_DONATIONS = 'FAILURE_FETCH_DONATIONS'; 4 | 5 | export const FETCH_DONATION = 'FETCH_DONATION'; 6 | export const SUCCESS_FETCH_DONATION = 'SUCCESS_FETCH_DONATION'; 7 | export const FAILURE_FETCH_DONATION = 'FAILURE_FETCH_DONATION'; 8 | -------------------------------------------------------------------------------- /frontend/actions/hospitals.js: -------------------------------------------------------------------------------- 1 | export const FETCH_HOSPITALS = 'FETCH_HOSPITALS'; 2 | export const SUCCESS_FETCH_HOSPITALS = 'SUCCESS_FETCH_HOSPITALS'; 3 | export const FAILURE_FETCH_HOSPITALS = 'FAILURE_FETCH_HOSPITALS'; 4 | 5 | export const FETCH_HOSPITAL = 'FETCH_HOSPITAL'; 6 | export const SUCCESS_FETCH_HOSPITAL = 'SUCCESS_FETCH_HOSPITAL'; 7 | export const FAILURE_FETCH_HOSPITAL = 'FAILURE_FETCH_HOSPITAL'; 8 | -------------------------------------------------------------------------------- /frontend/actions/localities.js: -------------------------------------------------------------------------------- 1 | export const FETCH_LOCALITIES = 'FETCH_LOCALITIES'; 2 | export const SUCCESS_FETCH_LOCALITIES = 'SUCCESS_FETCH_LOCALITIES'; 3 | export const FAILURE_FETCH_LOCALITIES = 'FAILURE_FETCH_LOCALITIES'; 4 | 5 | export const FETCH_LOCALITY = 'FETCH_LOCALITY'; 6 | export const SUCCESS_FETCH_LOCALITY = 'SUCCESS_FETCH_LOCALITY'; 7 | export const FAILURE_FETCH_LOCALITY = 'FAILURE_FETCH_LOCALITY'; -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /frontend/pages/about.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Layout from '../components/layout/Layout'; 3 | import AboutComponent from '../components/about'; 4 | 5 | class About extends Component { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | 15 | export default About 16 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env 21 | .idea 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /frontend/pages/contact.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Layout from '../components/layout/Layout'; 3 | import ContactsComponent from '../components/contacts'; 4 | 5 | class Contacts extends Component { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | } 14 | 15 | export default Contacts 16 | -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /template/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /frontend/components/about/BlockHeader.jsx: -------------------------------------------------------------------------------- 1 | import React, {Fragment} from "react"; 2 | import { Col, Row } from 'react-bootstrap' 3 | 4 | const BlockHeader = (props) => { 5 | return ( 6 | 7 | 8 | {props.children} 9 | 10 | 11 | 12 | ); 13 | } 14 | export default BlockHeader; -------------------------------------------------------------------------------- /frontend/actions/contacts.js: -------------------------------------------------------------------------------- 1 | export const FETCH_CONTACTS = 'FETCH_CONTACTS'; 2 | export const SUCCESS_FETCH_CONTACTS = 'SUCCESS_FETCH_CONTACTS'; 3 | export const FAILURE_FETCH_CONTACTS = 'FAILURE_FETCH_CONTACTS'; 4 | 5 | export const SEND_MESSAGE = 'SEND_MESSAGE'; 6 | export const SUCCESS_SEND_MESSAGE = 'SUCCESS_SEND_MESSAGE'; 7 | export const FAILURE_SEND_MESSAGE = 'FAILURE_SEND_MESSAGE'; 8 | 9 | export const INIT_CONTACT_FORM = 'INIT_CONTACT_FORM'; -------------------------------------------------------------------------------- /mobile/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Flutter", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /frontend/components/layout/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Footer = () => { 4 | return( 5 | 14 | ) 15 | }; 16 | 17 | export default Footer -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | ENV PYTHONDONTWRITEBYTECODE 1 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | WORKDIR /app 6 | 7 | RUN apt-get update && apt-get upgrade -y && apt-get install -y libpq-dev python3-dev binutils libproj-dev gdal-bin netcat 8 | RUN pip install -U pip setuptools 9 | 10 | COPY requirements.txt /app/ 11 | 12 | RUN pip install -r /app/requirements.txt 13 | 14 | ADD . /app/ 15 | 16 | EXPOSE 8000 17 | 18 | ENTRYPOINT ["/app/entrypoint.sh"] -------------------------------------------------------------------------------- /mobile/lib/models/response/AuthenticationResponse.dart: -------------------------------------------------------------------------------- 1 | import 'User.dart'; 2 | 3 | class AuthenticationResponse { 4 | final String token; 5 | final User user; 6 | 7 | AuthenticationResponse(this.token, this.user); 8 | 9 | factory AuthenticationResponse.fromJson(dynamic json) { 10 | var userJson = json['user'] as dynamic; 11 | var user = User.fromJson(userJson); 12 | return AuthenticationResponse(json['token'] as String, user); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /backend/backend/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for backend project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /frontend/pages/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import Layout from '../components/layout/Layout'; 3 | import dynamic from 'next/dynamic' 4 | 5 | const MainComponent = dynamic(() => import('../components/main'), {ssr: false}); 6 | 7 | class Home extends Component { 8 | render() { 9 | return ( 10 | 11 | 12 | 13 | ); 14 | } 15 | } 16 | 17 | export default Home 18 | -------------------------------------------------------------------------------- /.github/workflows/action.yml: -------------------------------------------------------------------------------- 1 | name: telegram message 2 | on: 3 | push: 4 | branches: master 5 | jobs: 6 | build: 7 | name: Build 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@master 11 | - name: send custom message with args 12 | uses: appleboy/telegram-action@master 13 | with: 14 | to: -477706553 15 | token: 971718801:AAGhQD8eN6xG4R9u2leF4BVzfl6F26wqVLQ 16 | args: В мастер-ветку прилетел новый коммит ^_^ 17 | -------------------------------------------------------------------------------- /mobile/android/app/src/main/java/com/example/flutterlogindemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.flutterlogindemo; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /mobile/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /frontend/pages/donations.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Layout from '../components/layout/Layout'; 3 | import DonationsComponent from '../components/donations' 4 | 5 | const Donations = (props) => ( 6 | 7 | 8 | 9 | ); 10 | 11 | Donations.getInitialProps = ({ query }) => { 12 | let data = {}; 13 | if (query.id) { 14 | data.id = query.id; 15 | } 16 | 17 | return data; 18 | } 19 | 20 | export default Donations; -------------------------------------------------------------------------------- /backend/common/schemas.py: -------------------------------------------------------------------------------- 1 | from rest_framework.schemas import AutoSchema 2 | 3 | 4 | class DefaultSchema(AutoSchema): 5 | def _allows_filters(self, path, method): 6 | """ 7 | Override to remove filter field on all actions but list 8 | """ 9 | if getattr(self.view, 'filter_backends', None) is None: 10 | return False 11 | 12 | if hasattr(self.view, 'action'): 13 | return self.view.action == 'list' 14 | 15 | return method.lower() == 'get' 16 | -------------------------------------------------------------------------------- /frontend/pages/_app.js: -------------------------------------------------------------------------------- 1 | import App from 'next/app'; 2 | import React from 'react'; 3 | import withReduxStore from '../config/with-redux-store'; 4 | import { Provider } from 'react-redux'; 5 | import '../styles/index.scss'; 6 | 7 | class MyApp extends App { 8 | render() { 9 | const { Component, pageProps, reduxStore } = this.props; 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | } 17 | 18 | export default withReduxStore(MyApp); -------------------------------------------------------------------------------- /mobile/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0024_auto_20200329_1508.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-29 15:08 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0023_update_search_fields'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='donationdetail', 15 | name='amount', 16 | field=models.PositiveIntegerField(verbose_name='Amount'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0041_auto_20200405_1717.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-05 17:17 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0040_auto_20200405_1659'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='distribution', 15 | name='receiver', 16 | field=models.TextField(blank=True, verbose_name='Кто принял?'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/DEPLOY.md: -------------------------------------------------------------------------------- 1 | Regular update: 2 | ``` 3 | yarn 4 | yarn build 5 | systemctl restart tirek-next 6 | ``` 7 | 8 | Systemd file 9 | ``` 10 | [Unit] 11 | Description=Nextjs frontend for tirek.kg 12 | After=network.target 13 | 14 | [Service] 15 | Environment=NODE_ENV=production 16 | Type=simple 17 | User=nodejs 18 | ExecStart=/usr/bin/node /opt/covid-supply/frontend/node_modules/.bin/next start --hostname=127.0.0.1 --port=3000 19 | WorkingDirectory=/opt/covid-supply/frontend 20 | Restart=on-failure 21 | 22 | [Install] 23 | WantedBy=multi-user.target 24 | ``` -------------------------------------------------------------------------------- /mobile/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0013_auto_20200327_2023.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-27 20:23 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0012_statistic'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='statistic', 15 | name='capacity', 16 | field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Capacity'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0056_auto_20200718_1902.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-07-18 19:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0055_fix_statistics_capacity'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='donation', 15 | name='created_at', 16 | field=models.DateTimeField(auto_now_add=True, verbose_name='Дата создания'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0040_auto_20200405_1659.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-05 16:59 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0039_distribution'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='distribution', 15 | options={'ordering': ('-date_of_distribute',), 'verbose_name': 'Запись для распределения', 'verbose_name_plural': 'Распределения'}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /mobile/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /mobile/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: tirek_mobile 2 | description: Tirek mobile. 3 | 4 | dependencies: 5 | flutter: 6 | sdk: flutter 7 | http: 0.12.1 8 | shared_preferences: 0.5.8 9 | cupertino_icons: ^0.1.2 10 | firebase_auth: ^0.14.0+5 11 | firebase_database: ^3.0.7 12 | flutter_speed_dial: ^1.2.1 13 | 14 | flutter_localizations: 15 | sdk: flutter 16 | 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | 22 | flutter: 23 | 24 | uses-material-design: true 25 | assets: 26 | - assets/tirek-logo.png 27 | - assets/logo.png 28 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0034_auto_20200402_1233.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-02 12:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0033_add_contact_info'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='hospital', 15 | name='code', 16 | field=models.CharField(help_text='Введите код', max_length=50, unique=True, verbose_name='Код'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0039_hospital_hidden.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-18 14:02 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0038_add_hospital_locations'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='hospital', 15 | name='hidden', 16 | field=models.BooleanField(default=False, help_text='Скрыть больницу?', verbose_name='Скрыть'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /frontend/pages/donations/[id].js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Layout from '../../components/layout/Layout'; 3 | import DistributionsComponent from '../../components/distributions' 4 | 5 | const Distributions = (props) => { 6 | return ( 7 | 8 | 9 | 10 | ); 11 | } 12 | 13 | Distributions.getInitialProps = ({ query }) => { 14 | let data = {}; 15 | if (query.id) { 16 | data.id = query.id; 17 | } 18 | 19 | return data; 20 | } 21 | 22 | export default Distributions; -------------------------------------------------------------------------------- /backend/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | import dotenv 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | 15 | dotenv.read_dotenv(os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')) 16 | 17 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 18 | 19 | application = get_wsgi_application() 20 | -------------------------------------------------------------------------------- /frontend/public/mdi_phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/styles/img/mdi_phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /backend/distributor/constants.py: -------------------------------------------------------------------------------- 1 | from django.utils.translation import ugettext as _ 2 | 3 | READY_TO_SEND = 'ready_to_sent' 4 | SENT = 'sent' 5 | DELIVERED = 'delivered' 6 | 7 | DISTRIBUTION_STATUSES = ( 8 | (READY_TO_SEND, _('Подготовлено')), 9 | (SENT, _('Отправлено')), 10 | (DELIVERED, _('Доставлено')), 11 | ) 12 | 13 | ORGANIZATION = 'ORGANIZATION' 14 | PERSON = 'PERSONAL' 15 | DONOR = 'DONOR' 16 | GOVERNMENT = 'GOVERNMENT' 17 | 18 | DONATOR_TYPES = ( 19 | (ORGANIZATION, _('ORGANIZATION')), 20 | (PERSON, _('PERSONAL')), 21 | (DONOR, _('DONOR')), 22 | (GOVERNMENT, _('GOVERNMENT')), 23 | ) -------------------------------------------------------------------------------- /frontend/config/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import loadInitialState from './loadInitialState'; 3 | 4 | import thunkMiddleware from 'redux-thunk'; 5 | import rootReducer from './rootReducer'; 6 | 7 | const initialState = loadInitialState(); 8 | 9 | const middleware = [thunkMiddleware]; 10 | const composedEnhancers = compose( 11 | applyMiddleware(...middleware) 12 | ); 13 | 14 | export function initializeStore() { 15 | let store; 16 | 17 | store = createStore( 18 | rootReducer, 19 | initialState, 20 | composedEnhancers 21 | ); 22 | 23 | return store; 24 | } -------------------------------------------------------------------------------- /frontend/public/mdi_pin_drop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/actions/creators/requests.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../requests'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | const { publicRuntimeConfig } = getConfig(); 5 | 6 | export const createRequest = (_data) => async dispatch => { 7 | dispatch({ type: Actions.CREATE_REQUEST }); 8 | try { 9 | const { data } = await axios.post(`${publicRuntimeConfig.apiUrl}/help-requests/`, _data); 10 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_CREATE_REQUEST, data })); 11 | } catch (err) { 12 | return Promise.resolve(dispatch({ type: Actions.FAILURE_CREATE_REQUEST })); 13 | } 14 | }; -------------------------------------------------------------------------------- /frontend/styles/img/mdi_pin_drop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/public/mdi_contact_phone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /frontend/components/navigation/ActiveLink.jsx: -------------------------------------------------------------------------------- 1 | import { withRouter } from 'next/router'; 2 | import Link from 'next/link'; 3 | import React, { Children } from 'react'; 4 | const ActiveLink = ({ router, children, ...props }) => { 5 | const child=Children.only(children); 6 | 7 | let className=child.props.className||''; 8 | if (router.pathname===props.href&&props.activeClassName) { 9 | className=`${className} ${props.activeClassName}`.trim(); 10 | } 11 | 12 | delete props.activeClassName; 13 | 14 | return{React.cloneElement(child, { className })}; 15 | }; 16 | 17 | export default withRouter(ActiveLink); -------------------------------------------------------------------------------- /template/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import 'bootstrap/dist/css/bootstrap.min.css'; 4 | import './index.css'; 5 | import App from './App'; 6 | import * as serviceWorker from './serviceWorker'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | 15 | // If you want your app to work offline and load faster, you can change 16 | // unregister() to register() below. Note this comes with some pitfalls. 17 | // Learn more about service workers: https://bit.ly/CRA-PWA 18 | serviceWorker.unregister(); 19 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0011_auto_20200326_2123.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-26 21:23 2 | 3 | import django.contrib.gis.db.models.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0010_auto_20200326_2053'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='hospital', 16 | name='location', 17 | field=django.contrib.gis.db.models.fields.PointField(help_text='To generate the map for your location', null=True, srid=4326), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /frontend/public/mdi_import_contacts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0023_update_search_fields.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-29 10:31 2 | 3 | from django.db import migrations, models 4 | 5 | from distributor.models import Hospital 6 | 7 | 8 | def update_hospital_search_fields(apps, schema_editor): 9 | hospitals = apps.get_model('distributor', 'Hospital').objects.all() 10 | for hospital in hospitals: 11 | hospital.save() 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [ 16 | ('distributor', '0022_auto_20200329_1031'), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(update_hospital_search_fields), 21 | ] 22 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0045_auto_20200420_1856.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 18:56 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0044_auto_20200420_1853'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='distributiondetail', 16 | name='distribution', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='distributor.Distribution', verbose_name='Распределение'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /mobile/README.md: -------------------------------------------------------------------------------- 1 | # tirek_mobile 2 | 3 | A sample Flutter project to show case user login and signup process with Firebase authentication. 4 | It also shows how to perform CRUD operations with Firebase Real Time Database using simple todo list application. 5 | 6 | ## Medium 7 | Do spend sometime to read the article to complement the source code: 8 | 9 | https://medium.com/flutterpub/flutter-how-to-do-user-login-with-firebase-a6af760b14d5 10 | https://medium.com/@tattwei46/flutter-how-to-do-crud-with-firebase-rtdb-ce61e3ce53a 11 | 12 | ## Getting Started 13 | 14 | For help getting started with Flutter, view our online 15 | [documentation](https://flutter.io/). 16 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0020_auto_20200328_1131.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-28 11:31 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0019_auto_20200328_0130'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='hospitalneeds', 16 | name='hospital', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='needs', to='distributor.Hospital', verbose_name='Hospital'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /frontend/actions/creators/distributions.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../distributions'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | const { publicRuntimeConfig } = getConfig(); 5 | 6 | export const fetchDistributions = (donationId) => async dispatch => { 7 | dispatch({ type: Actions.FETCH_DISTRIBUTIONS }); 8 | try { 9 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/distributions?donations=${donationId}`); 10 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_DISTRIBUTIONS, data })); 11 | } catch (err) { 12 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_DISTRIBUTIONS })); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /frontend/components/about/Block.jsx: -------------------------------------------------------------------------------- 1 | import React, {Fragment} from "react"; 2 | import { Col, Row, Container } from 'react-bootstrap' 3 | 4 | const Block = (props) => { 5 | const className = props.className ? props.className : 'about__block'; 6 | const filledClassName = props.filled ? `about__block_filled` : '' 7 | return ( 8 |
    9 | 10 | 11 | 12 | {props.children} 13 | 14 | 15 | 16 |
    17 | ); 18 | } 19 | export default Block; -------------------------------------------------------------------------------- /mobile/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.5.1' 9 | classpath 'com.google.gms:google-services:3.2.1' 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | jcenter() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /frontend/public/mdi_search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /mobile/lib/services/DonationService.dart: -------------------------------------------------------------------------------- 1 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 2 | import 'package:tirek_mobile/models/response/DonationResponse.dart'; 3 | 4 | abstract class DonationService { 5 | Future get(); 6 | } 7 | 8 | class TirekDonationService extends DonationService { 9 | @override 10 | Future get() async { 11 | final Map headers = { 12 | 'Content-Type': 'application/json; charset=utf-8', 13 | 'Accept': 'application/json', 14 | }; 15 | 16 | final responseJson = await ApiHelper.get("donations/", headers); 17 | 18 | return DonationResponse.fromJson(responseJson); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/Makefile: -------------------------------------------------------------------------------- 1 | include .env 2 | export $(shell sed 's/=.*//' .env) 3 | 4 | init: 5 | sudo apt-get install libpq-dev python3-dev 6 | sudo apt-get install binutils libproj-dev gdal-bin 7 | python3 -m venv venv 8 | 9 | install_deps: 10 | pip install -r requirements.txt 11 | 12 | env-up: 13 | docker-compose up 14 | 15 | end-down: 16 | docker-compose down 17 | 18 | activate: 19 | source venv/bin/activate 20 | 21 | start: 22 | python manage.py runserver 23 | 24 | createsuperuser: 25 | python manage.py createsuperuser 26 | 27 | migrate: 28 | python manage.py migrate 29 | 30 | runscript: 31 | python manage.py runscript ${SCRIPT} 32 | 33 | command: 34 | python manage.py ${COMMAND} 35 | -------------------------------------------------------------------------------- /template/src/img/mdi_search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0048_auto_20200420_1922.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 19:22 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0047_auto_20200420_1920'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='distributiondetail', 16 | name='distribution', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='distribution_detail', to='distributor.Distribution', verbose_name='Распределение'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /backend/scripts/add_user.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User, Group 2 | 3 | from distributor.models import Hospital 4 | 5 | 6 | def run(*args): 7 | for line in args: 8 | username, password = line.split(':') 9 | try: 10 | hospital = Hospital.objects.get(code=username) 11 | user = User.objects.create_user(username, password=password, is_superuser=False, is_staff=True) 12 | user.save() 13 | 14 | group = Group.objects.get(name='Hospital Managers') 15 | group.user_set.add(user) 16 | 17 | hospital.managers.add(user) 18 | except: 19 | print(f'Hospital with code {username} not found') 20 | -------------------------------------------------------------------------------- /backend/distributor/templatetags/tags.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from django.template import Library 4 | 5 | register = Library() 6 | 7 | DEFAULT_VERSION = 'Unknown' 8 | cache = {'version': DEFAULT_VERSION} 9 | 10 | 11 | @register.simple_tag(name='current_application_version') 12 | def get_current_application_version(): 13 | if cache['version'] == DEFAULT_VERSION: 14 | cache['version'] = get_git_revision_short_hash() 15 | return cache['version'] 16 | 17 | 18 | def get_git_revision_short_hash(): 19 | try: 20 | last_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']) 21 | return last_hash.strip().decode() 22 | except: 23 | return DEFAULT_VERSION 24 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0025_auto_20200329_1531.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-29 15:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0024_auto_20200329_1508'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='needtype', 15 | name='price_per_piece', 16 | ), 17 | migrations.AddField( 18 | model_name='donationdetail', 19 | name='price_per_piece', 20 | field=models.DecimalField(decimal_places=2, max_digits=12, null=True, verbose_name='Price per piece (KGS)'), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /mobile/lib/models/response/DonationResponse.dart: -------------------------------------------------------------------------------- 1 | class DonationResponse { 2 | final List donations; 3 | 4 | DonationResponse(this.donations); 5 | 6 | factory DonationResponse.fromJson(dynamic json) { 7 | var donations = []; 8 | for (var donationJson in json) { 9 | var donation = Donation.fromJson(donationJson); 10 | donations.add(donation); 11 | } 12 | return DonationResponse(donations); 13 | } 14 | } 15 | 16 | class Donation { 17 | final int id; 18 | final String donatorName; 19 | 20 | Donation(this.id, this.donatorName); 21 | 22 | factory Donation.fromJson(dynamic json) { 23 | return Donation(json['id'] as int, json['donator_name'] as String); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mobile/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0055_fix_statistics_capacity.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, transaction 2 | from django.db.models import Q 3 | 4 | 5 | def fix_statistics(apps, schema_editor): 6 | with transaction.atomic(): 7 | Statistic = apps.get_model('distributor', 'Statistic') 8 | statistics = Statistic.objects.filter(Q(has_capacity=True) & Q(capacity=None)) 9 | for statistic in statistics: 10 | statistic.has_capacity = False 11 | statistic.save() 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [ 16 | ('distributor', '0054_auto_20200426_1123'), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(fix_statistics) 21 | ] 22 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0016_auto_20200327_2112.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-27 21:12 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0015_add_statistic_categories'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='statistic', 15 | name='actual', 16 | field=models.IntegerField(verbose_name='Actual'), 17 | ), 18 | migrations.AlterField( 19 | model_name='statistic', 20 | name='capacity', 21 | field=models.IntegerField(blank=True, null=True, verbose_name='Capacity'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0007_auto_20200325_2322.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 23:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0006_auto_20200325_2234'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='needtype', 15 | name='name_ky', 16 | field=models.CharField(max_length=200, null=True, verbose_name='Name'), 17 | ), 18 | migrations.AddField( 19 | model_name='needtype', 20 | name='name_ru', 21 | field=models.CharField(max_length=200, null=True, verbose_name='Name'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /backend/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | import dotenv 6 | 7 | 8 | def main(): 9 | dotenv.read_dotenv() 10 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') 11 | try: 12 | from django.core.management import execute_from_command_line 13 | except ImportError as exc: 14 | raise ImportError( 15 | "Couldn't import Django. Are you sure it's installed and " 16 | "available on your PYTHONPATH environment variable? Did you " 17 | "forget to activate a virtual environment?" 18 | ) from exc 19 | execute_from_command_line(sys.argv) 20 | 21 | 22 | if __name__ == '__main__': 23 | main() 24 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0018_auto_20200327_2329.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-27 23:29 2 | 3 | import django.core.validators 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0017_helprequest'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='hospitalphonenumber', 16 | name='value', 17 | field=models.CharField(max_length=30, validators=[django.core.validators.RegexValidator(message='Phone number must be in these formats : 0555123456 or 03134 5 26 71 or 0312 45-26-71', regex='^[0]\\d{3,4}[- ]?\\d{1,2}[- ]?\\d{2}[- ]?\\d{2}$')], verbose_name='Phone Number'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /backend/users/services.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from django.db import IntegrityError 3 | from rest_framework.authtoken.models import Token 4 | from rest_framework.exceptions import NotFound, ValidationError 5 | 6 | User = get_user_model() 7 | 8 | 9 | class TokenService: 10 | model = Token 11 | 12 | @classmethod 13 | def get(cls, **filters): 14 | try: 15 | return cls.model.objects.get(**filters) 16 | except cls.model.DoesNotExist: 17 | raise NotFound('Token not found') 18 | 19 | @classmethod 20 | def logout(cls, user: User): 21 | token = cls.get(user=user) 22 | 23 | try: 24 | token.delete() 25 | except IntegrityError: 26 | raise ValidationError('Can not logout') 27 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0006_auto_20200325_2234.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 22:34 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0005_hospital'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='measure', 15 | name='name_ky', 16 | field=models.CharField(max_length=200, null=True, unique=True, verbose_name='Name (liters, kg, etc.)'), 17 | ), 18 | migrations.AddField( 19 | model_name='measure', 20 | name='name_ru', 21 | field=models.CharField(max_length=200, null=True, unique=True, verbose_name='Name (liters, kg, etc.)'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.2", 12 | "bootstrap": "^4.4.1", 13 | "dotenv": "^8.2.0", 14 | "i18next": "^19.3.4", 15 | "next": "9.3.2", 16 | "node-sass": "^4.13.1", 17 | "prop-types": "^15.7.2", 18 | "react": "16.13.1", 19 | "react-bootstrap": "^1.0.0", 20 | "react-dom": "16.13.1", 21 | "react-ga": "^2.7.0", 22 | "react-i18next": "^11.3.4", 23 | "react-redux": "^7.2.0", 24 | "react-select": "^3.1.0", 25 | "react-yandex-maps": "^4.3.0", 26 | "reaptcha": "1.7.2", 27 | "redux": "^4.0.5", 28 | "redux-thunk": "^2.3.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/public/zeit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0054_auto_20200426_1123.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-26 11:23 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0053_fix_distribution_details'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='hospitalneeds', 15 | options={'ordering': ['need_type__name'], 'verbose_name': 'Потребность', 'verbose_name_plural': 'Потребности больниц'}, 16 | ), 17 | migrations.AddField( 18 | model_name='hospitalneeds', 19 | name='request_amount_month', 20 | field=models.IntegerField(default=0, verbose_name='Требуемое количество в месяц'), 21 | preserve_default=False, 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /mobile/lib/exception/TirekException.dart: -------------------------------------------------------------------------------- 1 | class TirekException implements Exception { 2 | final message; 3 | final prefix; 4 | 5 | TirekException([this.message, this.prefix]); 6 | 7 | String toString() { 8 | return "$prefix$message"; 9 | } 10 | } 11 | 12 | class FetchDataException extends TirekException { 13 | FetchDataException([String message]) 14 | : super(message, "Ошибка при подключении: "); 15 | } 16 | 17 | class BadRequestException extends TirekException { 18 | BadRequestException([message]) : super(message, "Неверный запрос: "); 19 | } 20 | 21 | class UnauthorisedException extends TirekException { 22 | UnauthorisedException([message]) : super(message, "Неразрешенный: "); 23 | } 24 | 25 | class InvalidInputException extends TirekException { 26 | InvalidInputException([String message]) 27 | : super(message, "Некорректный ввод: "); 28 | } 29 | -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.2.7 2 | certifi==2019.11.28 3 | chardet==3.0.4 4 | coreapi==2.3.3 5 | coreschema==0.0.4 6 | Django==3.0.4 7 | django-3-jet==1.0.8 8 | django-admin-rangefilter==0.5.4 9 | django-appconf==1.0.4 10 | django-cacheops==4.2 11 | django-cors-headers==3.2.1 12 | django-dotenv==1.4.2 13 | django-extensions==2.2.8 14 | django-filter==2.2.0 15 | django-ipware==2.1.0 16 | django-leaflet==0.26.0 17 | django-modeltranslation==0.14.4 18 | django-redis==4.11.0 19 | djangorestframework==3.11.0 20 | djangorestframework-recaptcha==0.2.0 21 | funcy==1.14 22 | gunicorn==20.0.4 23 | idna==2.9 24 | itypes==1.1.0 25 | Jinja2==2.11.1 26 | Markdown==3.2.1 27 | MarkupSafe==1.1.1 28 | psycopg2==2.8.4 29 | pytz==2019.3 30 | PyYAML==5.3.1 31 | redis==3.4.1 32 | requests==2.23.0 33 | six==1.14.0 34 | sqlparse==0.3.1 35 | uritemplate==3.0.1 36 | urllib3==1.25.8 37 | django-rest-auth==0.9.5 -------------------------------------------------------------------------------- /backend/distributor/migrations/0036_add_statistic_category.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | def add_statistic_category(apps, schema_editor): 5 | StatisticCategory = apps.get_model('distributor', 'StatisticCategory') 6 | 7 | try: 8 | statistic_category = StatisticCategory.objects.get(name='Персонала') 9 | statistic_category.name = 'Врачи' 10 | statistic_category.save() 11 | except StatisticCategory.DoesNotExist: 12 | print("CategoryStatistic with name {} not found".format('Персонала')) 13 | 14 | StatisticCategory.objects.create(name='Средний медперсонал', name_ru='Средний медперсонал') 15 | 16 | 17 | class Migration(migrations.Migration): 18 | dependencies = [ 19 | ('distributor', '0035_import_hospitals_addresses'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(add_statistic_category), 24 | ] 25 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0052_auto_20200422_2007.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-22 20:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0051_add_manager_group'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='donation', 15 | name='total_price', 16 | field=models.DecimalField(blank=True, decimal_places=2, help_text='В сомах', max_digits=20, null=True, verbose_name='Общая сумма пожертвования'), 17 | ), 18 | migrations.AlterField( 19 | model_name='donationdetail', 20 | name='total_cost', 21 | field=models.DecimalField(blank=True, decimal_places=2, help_text='Цена в сомах', max_digits=20, null=True, verbose_name='Общая стоимость'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0050_auto_20200420_1852.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 18:52 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ('distributor', '0049_auto_20200420_1937'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='hospital', 17 | name='managers', 18 | field=models.ManyToManyField(blank=True, related_name='hospitals', to=settings.AUTH_USER_MODEL), 19 | ), 20 | migrations.AlterField( 21 | model_name='hospital', 22 | name='hidden', 23 | field=models.BooleanField(default=False, help_text='Скрыть больницу?', verbose_name='Скрыто'), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /mobile/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /backend/users/serializers.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | from rest_auth.models import TokenModel 3 | from rest_framework import serializers 4 | 5 | User = get_user_model() 6 | 7 | 8 | class UserShortSerializer(serializers.ModelSerializer): 9 | full_name = serializers.SerializerMethodField() 10 | 11 | class Meta: 12 | model = User 13 | fields = ('id', 'full_name') 14 | 15 | def get_full_name(self, user): 16 | return "{first_name} {last_name}".format( 17 | first_name=user.first_name, 18 | last_name=user.last_name 19 | ) 20 | 21 | 22 | class TokenSerializer(serializers.ModelSerializer): 23 | """ 24 | Serializer for Token model. 25 | """ 26 | 27 | token = serializers.CharField(source='key') 28 | 29 | user = UserShortSerializer() 30 | 31 | class Meta: 32 | model = TokenModel 33 | fields = ('token', 'user') 34 | -------------------------------------------------------------------------------- /frontend/config/rootReducer.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux'; 2 | import districtsReducer from '../reducers/districtsReducer'; 3 | import donationsReducer from '../reducers/donationsReducer'; 4 | import hospitalsReducer from '../reducers/hospitalsReducer'; 5 | import localitiesReducer from '../reducers/localitiesReducer'; 6 | import regionsReducer from '../reducers/regionsReducer'; 7 | import requestsReducer from '../reducers/requestsReducer'; 8 | import contactsReducer from '../reducers/contactsReducer'; 9 | import distributionsReducer from '../reducers/distributionsReducer'; 10 | 11 | export default combineReducers({ 12 | districts: districtsReducer, 13 | donations: donationsReducer, 14 | hospitals: hospitalsReducer, 15 | localities: localitiesReducer, 16 | regions: regionsReducer, 17 | requests: requestsReducer, 18 | contacts: contactsReducer, 19 | distributions: distributionsReducer, 20 | }); 21 | -------------------------------------------------------------------------------- /backend/users/views.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ObjectDoesNotExist 2 | from rest_framework import status 3 | from rest_framework.authentication import TokenAuthentication 4 | from rest_framework.permissions import IsAuthenticated 5 | from rest_framework.response import Response 6 | from rest_framework.views import APIView 7 | from django.utils.translation import ugettext_lazy as _ 8 | 9 | from users.services import TokenService 10 | 11 | 12 | class LogoutView(APIView): 13 | permission_classes = (IsAuthenticated,) 14 | authentication_classes = (TokenAuthentication,) 15 | 16 | def post(self, request, *args, **kwargs): 17 | return self.logout(request) 18 | 19 | def logout(self, request): 20 | TokenService.logout(user=request.user) 21 | 22 | response = Response({"message": _("Successfully logged out.")}, 23 | status=status.HTTP_200_OK) 24 | return response 25 | -------------------------------------------------------------------------------- /backend/distributor/filters.py: -------------------------------------------------------------------------------- 1 | from django_filters import Filter, FilterSet 2 | from rest_framework.exceptions import ValidationError 3 | 4 | from .models import Distribution 5 | 6 | 7 | class MultipleListFilter(Filter): 8 | MAX_LIMIT = 20 9 | 10 | def filter(self, qs, value): 11 | if not value: 12 | return qs 13 | 14 | self.lookup_expr = 'in' 15 | values = value.split(',') 16 | 17 | if len(values) > self.MAX_LIMIT: 18 | raise ValidationError('Max number of ids should be less than equal 20') 19 | 20 | return super(MultipleListFilter, self).filter(qs, values).distinct() 21 | 22 | 23 | class DistributionFilter(FilterSet): 24 | """ 25 | Available filter route /api/v1/distributions/?donation=1,2,3 26 | """ 27 | donations = MultipleListFilter(field_name='donation') 28 | 29 | class Meta: 30 | model = Distribution 31 | fields = ('donations',) 32 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0015_add_statistic_categories.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, transaction 2 | 3 | from distributor.models import StatisticCategory 4 | 5 | 6 | def add_statistic_categories(apps, schema_editor): 7 | categories = [ 8 | "Персонала", 9 | "Волентёров", 10 | "Коек всего", 11 | "Обр. по коронавирусу", 12 | "Больные вирусом в больнице", 13 | "Выздоровевшие в больнице", 14 | "Умершие в больнице", 15 | ] 16 | 17 | with transaction.atomic(): 18 | for category in categories: 19 | statistic_category = StatisticCategory(name=category, name_ru=category) 20 | statistic_category.save() 21 | 22 | 23 | class Migration(migrations.Migration): 24 | dependencies = [ 25 | ('distributor', '0014_auto_20200327_2104'), 26 | ] 27 | 28 | operations = [ 29 | migrations.RunPython(add_statistic_categories), 30 | ] 31 | -------------------------------------------------------------------------------- /mobile/lib/services/AuthenticationService.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 3 | import 'package:tirek_mobile/models/response/AuthenticationResponse.dart'; 4 | import 'dart:convert'; 5 | 6 | abstract class AuthenticationService { 7 | Future login(String username, String password); 8 | } 9 | 10 | class TirekAuthenticationService implements AuthenticationService { 11 | @override 12 | Future login(String username, String password) async { 13 | final body = json.encode({"username": username, "password": password}); 14 | 15 | final Map headers = { 16 | 'Content-Type': 'application/json; charset=utf-8', 17 | 'Accept': 'application/json', 18 | }; 19 | 20 | final responseJson = await ApiHelper.post("auth/login/", headers, body); 21 | 22 | return AuthenticationResponse.fromJson(responseJson); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0033_add_contact_info.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-31 18:50 2 | 3 | import django.core.validators 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | from distributor.models import ContactInfo 8 | 9 | 10 | def create_contact_info(apps, schema_editor): 11 | contact_info = ContactInfo(text='По всем вопросам вы можете с нами связаться по телефонам', 12 | text_ru='По всем вопросам вы можете с нами связаться по телефонам', 13 | text_ky='По всем вопросам вы можете с нами связаться по телефонам') 14 | contact_info.save() 15 | 16 | 17 | class Migration(migrations.Migration): 18 | dependencies = [ 19 | ('distributor', '0029_contactinfo_contactinfoemail_contactinfophonenumber_contactmessage'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(create_contact_info), 24 | ] 25 | -------------------------------------------------------------------------------- /frontend/reducers/distributionsReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/distributions' 2 | 3 | const initialState = { 4 | fetching: false, 5 | count: 0, 6 | results: [] 7 | }; 8 | 9 | export default function distributions(state = initialState, action) { 10 | switch (action.type) { 11 | case Actions.FETCH_DISTRIBUTIONS: 12 | return { 13 | ...state, 14 | fetching: true 15 | }; 16 | 17 | case Actions.SUCCESS_FETCH_DISTRIBUTIONS: 18 | return { 19 | ...state, 20 | fetching: false, 21 | count: action.data.count, 22 | results: action.data.results 23 | }; 24 | 25 | case Actions.FAILURE_FETCH_DISTRIBUTIONS: 26 | return { 27 | ...state, 28 | fetching: false 29 | }; 30 | 31 | default: 32 | return state; 33 | } 34 | } -------------------------------------------------------------------------------- /backend/distributor/migrations/0022_auto_20200329_1031.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-29 10:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0021_page'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='hospital', 15 | name='search_district_id', 16 | field=models.IntegerField(null=True, verbose_name='Search District ID'), 17 | ), 18 | migrations.AddField( 19 | model_name='hospital', 20 | name='search_locality_id', 21 | field=models.IntegerField(null=True, verbose_name='Search Locality ID'), 22 | ), 23 | migrations.AddField( 24 | model_name='hospital', 25 | name='search_region_id', 26 | field=models.IntegerField(null=True, verbose_name='Search Region ID'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0043_auto_20200405_1813.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-05 18:13 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0042_auto_20200405_1807'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='distribution', 15 | options={'ordering': ('-distributed_at',), 'verbose_name': 'Запись для распределения', 'verbose_name_plural': 'Распределения'}, 16 | ), 17 | migrations.RenameField( 18 | model_name='distribution', 19 | old_name='date_of_distribute', 20 | new_name='distributed_at', 21 | ), 22 | migrations.AddField( 23 | model_name='distribution', 24 | name='delivered_at', 25 | field=models.DateField(blank=True, null=True, verbose_name='Дата доставки'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0042_auto_20200405_1807.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-05 18:07 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0041_auto_20200405_1717'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='distribution', 16 | name='hospital', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='distributions', to='distributor.Hospital', verbose_name='Больница'), 18 | ), 19 | migrations.AlterField( 20 | model_name='distribution', 21 | name='status', 22 | field=models.CharField(choices=[('ready_to_sent', 'Подготовлено'), ('sent', 'Отправлено'), ('delivered', 'Доставлено')], default='ready_to_sent', max_length=20, verbose_name='Статус'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0047_auto_20200420_1920.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 19:20 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0046_merge_20200420_1908'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='distributiondetail', 15 | name='price_per_piece', 16 | ), 17 | migrations.AddField( 18 | model_name='distributiondetail', 19 | name='total_cost', 20 | field=models.DecimalField(decimal_places=2, default=0.0, help_text='Цена в сомах', max_digits=20, verbose_name='Общая стоимость'), 21 | ), 22 | migrations.AlterField( 23 | model_name='hospital', 24 | name='hidden', 25 | field=models.BooleanField(default=False, help_text='Скрыть больницу?', verbose_name='Скрыто'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "covid", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "bootstrap": "^4.4.1", 10 | "google-map-react": "^1.1.7", 11 | "react": "^16.13.1", 12 | "react-bootstrap": "^1.0.0", 13 | "react-dom": "^16.13.1", 14 | "react-scripts": "3.4.1" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Backend Environment 4 | 5 | ``` 6 | sudo apt-get install libpq-dev python3-dev 7 | sudo apt-get install binutils libproj-dev gdal-bin 8 | ``` 9 | 10 | Run `cp .env.dev .env` and make sure it contains what you need :) 11 | 12 | ``` 13 | docker-compose up -d #make sure that db works :) 14 | ``` 15 | 16 | ``` 17 | 1. git clone git@github.com:Entea/covid-supply-info.git ~/projects/covid-supply-info 18 | 2. cd ~/projects/covid-supply-info/backend 19 | 3. python3 -m venv venv 20 | 4. source venv/bin/activate 21 | 5. pip install -r requirements.txt 22 | 6. python manage.py runserver 23 | ``` 24 | 25 | #### How to run migrations? 26 | ``` 27 | python manage.py migrate 28 | ``` 29 | or 30 | ``` 31 | docker compose exec make migrate 32 | ``` 33 | 34 | #### How to create an admin? 35 | ``` 36 | python manage.py createsuperuser 37 | ``` 38 | or 39 | ``` 40 | docker-compose exec make createsuperuser 41 | ``` 42 | 43 | #### How to update dependencies? 44 | ``` 45 | pip freeze > requirements.txt 46 | ``` 47 | -------------------------------------------------------------------------------- /mobile/lib/models/response/NeedsTypeResponse.dart: -------------------------------------------------------------------------------- 1 | import 'Measure.dart'; 2 | 3 | class NeedsTypeResponse { 4 | final int count; 5 | 6 | final List needsTypes; 7 | 8 | NeedsTypeResponse(this.count, this.needsTypes); 9 | 10 | factory NeedsTypeResponse.fromJson(dynamic json) { 11 | var results = json['results'] as dynamic; 12 | var needsTypes = []; 13 | for (var result in results) { 14 | var needsType = NeedsType.fromJson(result); 15 | needsTypes.add(needsType); 16 | } 17 | return NeedsTypeResponse(json['count'] as int, needsTypes); 18 | } 19 | } 20 | 21 | class NeedsType { 22 | final int id; 23 | final String name; 24 | final Measure measure; 25 | 26 | NeedsType(this.id, this.name, this.measure); 27 | 28 | factory NeedsType.fromJson(dynamic json) { 29 | var measureJson = json['measure'] as dynamic; 30 | var measure = Measure.fromJson(measureJson); 31 | return NeedsType(json['id'] as int, json['name'] as String, measure); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/actions/creators/regions.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../regions'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | const { publicRuntimeConfig } = getConfig(); 5 | 6 | export const fetchRegions = () => async dispatch => { 7 | dispatch({ type: Actions.FETCH_REGIONS }); 8 | try { 9 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/regions/`); 10 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_REGIONS, data })); 11 | } catch (err) { 12 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_REGIONS })); 13 | } 14 | }; 15 | 16 | export const fetchRegion = (regionId) => async dispatch => { 17 | dispatch({ type: Actions.FETCH_REGION }); 18 | try { 19 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/regions/${regionId}/`); 20 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_REGION, data })); 21 | } catch (err) { 22 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_REGION })); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0005_hospital.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 20:22 2 | 3 | import django.contrib.gis.db.models.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0004_donation_donationdetail'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Hospital', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=200, verbose_name='Name')), 19 | ('code', models.CharField(max_length=50, verbose_name='Code')), 20 | ('phone_number', models.CharField(max_length=20, verbose_name='Phone Number')), 21 | ('location', django.contrib.gis.db.models.fields.PointField(help_text='To generate the map for your location', srid=4326)), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /frontend/config/loadInitialState.js: -------------------------------------------------------------------------------- 1 | import * as status from '../constants/messageStatus'; 2 | 3 | export default () => { 4 | return { 5 | districts: { 6 | fetching: false, 7 | count: 0, 8 | results: [], 9 | single: {} 10 | }, 11 | donations: { 12 | fetching: false, 13 | count: 0, 14 | results: [], 15 | single: {} 16 | }, 17 | hospitals: { 18 | fetching: false, 19 | count: 0, 20 | results: [], 21 | single: {} 22 | }, 23 | localities: { 24 | fetching: false, 25 | count: 0, 26 | results: [], 27 | single: {} 28 | }, 29 | regions: { 30 | fetching: false, 31 | count: 0, 32 | results: [], 33 | single: {} 34 | }, 35 | requests: { 36 | sending: false, 37 | requestStatus: 0, 38 | regionFetching: false, 39 | districtFetching: false, 40 | localityFetching: false, 41 | districts:[], 42 | regions:[], 43 | localities:[], 44 | }, 45 | contacts: { 46 | sending: false, 47 | status: status.INIT, 48 | fetching: false, 49 | data: {} 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /backend/distributor/migrations/0049_auto_20200420_1937.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 19:37 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0048_auto_20200420_1922'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='distributiondetail', 16 | name='distribution', 17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='distribution_details', to='distributor.Distribution', verbose_name='Распределение'), 18 | ), 19 | migrations.AlterField( 20 | model_name='distributiondetail', 21 | name='donation', 22 | field=models.ForeignKey(help_text='Выберите ранее созданное пожертвование', on_delete=django.db.models.deletion.PROTECT, related_name='donation_details', to='distributor.Donation', verbose_name='Пожертвование)'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /backend/static/js/statistic.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | (function ($) { 3 | function disable(checkbox) { 4 | checkbox.parent().parent('tr').find('.field-capacity > input[type="number"]').addClass('disabled'); 5 | } 6 | 7 | function enable(checkbox) { 8 | checkbox.parent().parent('tr').find('.field-capacity > input[type="number"]').removeClass('disabled'); 9 | } 10 | 11 | $('.field-has_capacity > input[type="checkbox"]').on('click', function () { 12 | const checkbox = $(this); 13 | if (checkbox.is(":checked")) { 14 | enable(checkbox); 15 | } else { 16 | disable(checkbox); 17 | } 18 | }); 19 | 20 | $('.field-has_capacity > input[type="checkbox"]').each(function (index, elem) { 21 | const checkbox = $(elem); 22 | if (!checkbox.is(":checked")) { 23 | disable(checkbox); 24 | } 25 | }) 26 | 27 | })(django.jQuery); 28 | }); -------------------------------------------------------------------------------- /frontend/actions/creators/donations.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../donations'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | const { publicRuntimeConfig } = getConfig(); 5 | 6 | export const fetchDonations = () => async dispatch => { 7 | dispatch({ type: Actions.FETCH_DONATIONS }); 8 | try { 9 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/donations/`); 10 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_DONATIONS, data })); 11 | } catch (err) { 12 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_DONATIONS })); 13 | } 14 | }; 15 | 16 | export const fetchDonation = (donationId) => async dispatch => { 17 | dispatch({ type: Actions.FETCH_DONATION }); 18 | try { 19 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/donations/${donationId}/`); 20 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_DONATION, data })); 21 | } catch (err) { 22 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_DONATION })); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /mobile/lib/services/NeedsService.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 3 | 4 | import 'package:tirek_mobile/models/response/NeedsResponse.dart'; 5 | import 'package:tirek_mobile/services/SharedPreferencesService.dart'; 6 | 7 | abstract class NeedsService { 8 | Future get(); 9 | } 10 | 11 | class TirekNeedsService implements NeedsService { 12 | final SharedPreferencesService sharedPreferencesService; 13 | 14 | TirekNeedsService(this.sharedPreferencesService); 15 | 16 | @override 17 | Future get() async { 18 | final userInfo = await sharedPreferencesService.getCurrentUserInfo(); 19 | 20 | final Map headers = { 21 | 'Authorization': "Token ${userInfo.token}", 22 | 'Content-Type': 'application/json; charset=utf-8', 23 | 'Accept': 'application/json; charset=utf-8', 24 | }; 25 | 26 | final responseJson = await ApiHelper.get("hospital-needs/", headers); 27 | 28 | return NeedsResponse.fromJson(responseJson); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mobile/lib/models/response/NeedsResponse.dart: -------------------------------------------------------------------------------- 1 | class NeedsResponse { 2 | final int count; 3 | 4 | final List needs; 5 | 6 | NeedsResponse(this.count, this.needs); 7 | 8 | factory NeedsResponse.fromJson(dynamic json) { 9 | var results = json['results'] as dynamic; 10 | 11 | var needs = []; 12 | for (var result in results) { 13 | var need = NeedsItem.fromJson(result); 14 | needs.add(need); 15 | } 16 | return NeedsResponse(json['count'] as int, needs); 17 | } 18 | } 19 | 20 | class NeedsItem { 21 | final String need_type; 22 | final Object hospital; 23 | final int reserve_amount; 24 | final int request_amount; 25 | final String created_at; 26 | 27 | NeedsItem(this.need_type, this.hospital, this.request_amount, this.reserve_amount, this.created_at); 28 | 29 | factory NeedsItem.fromJson(dynamic json) { 30 | return NeedsItem(json['need_type']['name'] as String, json['hospital'] as Object, json['request_amount'] as int, json['reserve_amount'] as int, json['created_at'] as String); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mobile/lib/models/response/HospitalResponse.dart: -------------------------------------------------------------------------------- 1 | class HospitalResponse { 2 | final int count; 3 | 4 | final List hospitals; 5 | 6 | HospitalResponse(this.count, this.hospitals); 7 | 8 | factory HospitalResponse.fromJson(dynamic json) { 9 | var results = json['results'] as dynamic; 10 | var hospitals = []; 11 | for (var result in results) { 12 | var hospital = Hospital.fromJson(result); 13 | hospitals.add(hospital); 14 | } 15 | return HospitalResponse(json['count'] as int, hospitals); 16 | } 17 | } 18 | 19 | class Hospital { 20 | final int id; 21 | final String name; 22 | final String code; 23 | final double indicator; 24 | final String address; 25 | 26 | Hospital(this.id, this.name, this.code, this.indicator, this.address); 27 | 28 | factory Hospital.fromJson(dynamic json) { 29 | return Hospital( 30 | json['id'] as int, 31 | json['name'] as String, 32 | json['code'] as String, 33 | json['indicator'] as double, 34 | json['address'] as String); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frontend/utils/ga.js: -------------------------------------------------------------------------------- 1 | import ReactGA from 'react-ga' 2 | import getConfig from 'next/config'; 3 | 4 | const {publicRuntimeConfig} = getConfig(); 5 | 6 | export const initGA = () => { 7 | if (publicRuntimeConfig.gaKey) { 8 | ReactGA.initialize(publicRuntimeConfig.gaKey) 9 | } else { 10 | console.log('Skipping GA init'); 11 | } 12 | } 13 | 14 | export const logPageView = () => { 15 | if (publicRuntimeConfig.gaKey) { 16 | console.log('Got a page view'); 17 | ReactGA.set({page: window.location.pathname}) 18 | ReactGA.pageview(window.location.pathname) 19 | } 20 | } 21 | 22 | export const logEvent = (category = '', action = '') => { 23 | if (publicRuntimeConfig.gaKey) { 24 | if (category && action) { 25 | ReactGA.event({category, action}) 26 | } 27 | } 28 | } 29 | 30 | export const logException = (description = '', fatal = false) => { 31 | if (publicRuntimeConfig.gaKey) { 32 | if (description) { 33 | ReactGA.exception({description, fatal}) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mobile/lib/services/HospitalService.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 3 | 4 | import 'package:tirek_mobile/models/response/HospitalResponse.dart'; 5 | import 'package:tirek_mobile/services/SharedPreferencesService.dart'; 6 | 7 | abstract class HospitalService { 8 | Future get(); 9 | } 10 | 11 | class TirekHospitalService implements HospitalService { 12 | final SharedPreferencesService sharedPreferencesService; 13 | 14 | TirekHospitalService(this.sharedPreferencesService); 15 | 16 | @override 17 | Future get() async { 18 | final userInfo = await sharedPreferencesService.getCurrentUserInfo(); 19 | 20 | final Map headers = { 21 | 'Authorization': "Token ${userInfo.token}", 22 | 'Content-Type': 'application/json; charset=utf-8', 23 | 'Accept': 'application/json; charset=utf-8', 24 | }; 25 | 26 | final responseJson = await ApiHelper.get("managers/hospitals/", headers); 27 | 28 | return HospitalResponse.fromJson(responseJson); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mobile/lib/services/NeedsTypeService.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 3 | 4 | import 'package:tirek_mobile/models/response/NeedsTypeResponse.dart'; 5 | import 'package:tirek_mobile/services/SharedPreferencesService.dart'; 6 | 7 | abstract class NeedsTypeService { 8 | Future get(); 9 | } 10 | 11 | class TirekNeedsTypeService implements NeedsTypeService { 12 | final SharedPreferencesService sharedPreferencesService; 13 | 14 | TirekNeedsTypeService(this.sharedPreferencesService); 15 | 16 | @override 17 | Future get() async { 18 | final userInfo = await sharedPreferencesService.getCurrentUserInfo(); 19 | 20 | final Map headers = { 21 | 'Authorization': "Token ${userInfo.token}", 22 | 'Content-Type': 'application/json; charset=utf-8', 23 | 'Accept': 'application/json; charset=utf-8', 24 | }; 25 | 26 | final responseJson = await ApiHelper.get("need-types/", headers); 27 | 28 | return NeedsTypeResponse.fromJson(responseJson); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /mobile/lib/services/LogoutService.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 3 | import 'package:tirek_mobile/models/response/LogoutResponse.dart'; 4 | import 'package:tirek_mobile/services/SharedPreferencesService.dart'; 5 | import 'dart:convert'; 6 | 7 | abstract class LogoutService { 8 | Future logout(); 9 | } 10 | 11 | class TirekLogoutService implements LogoutService { 12 | final SharedPreferencesService sharedPreferencesService; 13 | 14 | TirekLogoutService(this.sharedPreferencesService); 15 | 16 | @override 17 | Future logout() async { 18 | final userInfo = await sharedPreferencesService.getCurrentUserInfo(); 19 | final body = json.encode({}); 20 | 21 | final Map headers = { 22 | 'Authorization': "Token ${userInfo.token}", 23 | 'Content-Type': 'application/json; charset=utf-8', 24 | 'Accept': 'application/json', 25 | }; 26 | 27 | await ApiHelper.post("logout/", headers, body); 28 | await sharedPreferencesService.removeCurrentUserInfo(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/reducers/donationsReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/donations' 2 | 3 | const initialState = { 4 | fetching: false, 5 | count: 0, 6 | results: [], 7 | single: {} 8 | }; 9 | 10 | export default function donations(state = initialState, action) { 11 | switch (action.type) { 12 | case Actions.FETCH_DONATIONS: 13 | return { 14 | ...state, 15 | fetching: true 16 | }; 17 | 18 | case Actions.SUCCESS_FETCH_DONATIONS: 19 | return { 20 | ...state, 21 | fetching: false, 22 | results: action.data 23 | }; 24 | 25 | case Actions.FAILURE_FETCH_DONATIONS: 26 | return { 27 | ...state, 28 | fetching: false 29 | }; 30 | 31 | case Actions.FETCH_DONATION: 32 | return { 33 | ...state, 34 | fetching: true 35 | }; 36 | 37 | case Actions.SUCCESS_FETCH_DONATION: 38 | return { 39 | ...state, 40 | fetching: false, 41 | single: action.data 42 | }; 43 | 44 | case Actions.FAILURE_FETCH_DONATION: 45 | return { 46 | ...state, 47 | fetching: false 48 | }; 49 | 50 | default: 51 | return state; 52 | } 53 | } -------------------------------------------------------------------------------- /frontend/styles/about.scss: -------------------------------------------------------------------------------- 1 | .about { 2 | margin-top: 82px; 3 | 4 | &__block { 5 | padding: 3.5rem 0px; 6 | min-height: 25rem; 7 | 8 | &_filled { 9 | background-color: #0fb4d2; 10 | color: white; 11 | } 12 | 13 | &_header { 14 | margin: 0 auto; 15 | margin-bottom: 2rem; 16 | font-weight: 700; 17 | line-height: 1.2; 18 | font-size: 1.4rem; 19 | text-transform: uppercase; 20 | text-align: center; 21 | } 22 | &_content { 23 | max-width: 800px; 24 | margin: 0 auto; 25 | padding: 15px; 26 | line-height: 2rem; 27 | font-size: 1.3rem; 28 | text-align: justify; 29 | } 30 | } 31 | 32 | &__feature_images { 33 | margin-top: 1rem; 34 | img { 35 | padding: 2rem; 36 | } 37 | } 38 | } 39 | 40 | .small-text { 41 | font-size: 1rem; 42 | line-height: 1.2rem; 43 | padding: 30px 10px; 44 | text-align: justify; 45 | } 46 | -------------------------------------------------------------------------------- /mobile/lib/services/DistributionsService.dart: -------------------------------------------------------------------------------- 1 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 2 | import 'package:tirek_mobile/models/response/DistributionResponse.dart'; 3 | 4 | import 'SharedPreferencesService.dart'; 5 | 6 | abstract class DistributionsService { 7 | Future getManagerDistributions(); 8 | } 9 | 10 | class TirekDistributionsService extends DistributionsService { 11 | final SharedPreferencesService sharedPreferencesService; 12 | 13 | TirekDistributionsService(this.sharedPreferencesService); 14 | 15 | @override 16 | Future getManagerDistributions() async { 17 | final userInfo = await sharedPreferencesService.getCurrentUserInfo(); 18 | 19 | final Map headers = { 20 | 'Authorization': "Token ${userInfo.token}", 21 | 'Content-Type': 'application/json; charset=utf-8', 22 | 'Accept': 'application/json; charset=utf-8', 23 | }; 24 | 25 | final responseJson = 26 | await ApiHelper.get("managers/distributions/", headers); 27 | 28 | return DistributionResponse.fromJson(responseJson); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/reducers/regionsReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/regions' 2 | 3 | const initialState = { 4 | fetching: false, 5 | count: 0, 6 | results: [], 7 | single: {} 8 | }; 9 | 10 | export default function hospitals(state = initialState, action) { 11 | switch (action.type) { 12 | case Actions.FETCH_REGIONS: 13 | return { 14 | ...state, 15 | fetching: true 16 | }; 17 | 18 | case Actions.SUCCESS_FETCH_REGIONS: 19 | return { 20 | ...state, 21 | fetching: false, 22 | count: action.data.length, 23 | results: action.data 24 | }; 25 | 26 | case Actions.FAILURE_FETCH_REGIONS: 27 | return { 28 | ...state, 29 | fetching: false 30 | }; 31 | 32 | case Actions.FETCH_REGION: 33 | return { 34 | ...state, 35 | fetching: true 36 | }; 37 | 38 | case Actions.SUCCESS_FETCH_REGION: 39 | return { 40 | ...state, 41 | fetching: false, 42 | single: action.data 43 | }; 44 | 45 | case Actions.FAILURE_FETCH_REGION: 46 | return { 47 | ...state, 48 | fetching: false 49 | }; 50 | 51 | default: 52 | return state; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /mobile/lib/models/response/DistributionResponse.dart: -------------------------------------------------------------------------------- 1 | import 'package:tirek_mobile/models/response/HospitalResponse.dart'; 2 | 3 | class DistributionResponse { 4 | final int count; 5 | final List distributions; 6 | 7 | DistributionResponse(this.count, this.distributions); 8 | 9 | factory DistributionResponse.fromJson(dynamic json) { 10 | var results = json['results'] as dynamic; 11 | var distributions = []; 12 | for (var result in results) { 13 | var distribution = Distribution.fromJson(result); 14 | distributions.add(distribution); 15 | } 16 | return DistributionResponse(json['count'] as int, distributions); 17 | } 18 | } 19 | 20 | class Distribution { 21 | final int id; 22 | final Hospital hospital; 23 | final String sender; 24 | final String receiver; 25 | 26 | Distribution(this.id, this.hospital, this.sender, this.receiver); 27 | 28 | factory Distribution.fromJson(dynamic json) { 29 | return Distribution(json['id'] as int, Hospital.fromJson(json['hospital']), 30 | json['sender'] as String, json['receiver'] as String); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /frontend/reducers/districtsReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/districts' 2 | 3 | const initialState = { 4 | fetching: false, 5 | count: 0, 6 | results: [], 7 | single: {} 8 | }; 9 | 10 | export default function districts(state = initialState, action) { 11 | switch (action.type) { 12 | case Actions.FETCH_DISTRICTS: 13 | return { 14 | ...state, 15 | fetching: true 16 | }; 17 | 18 | case Actions.SUCCESS_FETCH_DISTRICTS: 19 | return { 20 | ...state, 21 | fetching: false, 22 | count: action.data.length, 23 | results: action.data 24 | }; 25 | 26 | case Actions.FAILURE_FETCH_DISTRICTS: 27 | return { 28 | ...state, 29 | fetching: false 30 | }; 31 | 32 | case Actions.FETCH_DISTRICT: 33 | return { 34 | ...state, 35 | fetching: true 36 | }; 37 | 38 | case Actions.SUCCESS_FETCH_DISTRICT: 39 | return { 40 | ...state, 41 | fetching: false, 42 | single: action.data 43 | }; 44 | 45 | case Actions.FAILURE_FETCH_DISTRICT: 46 | return { 47 | ...state, 48 | fetching: false 49 | }; 50 | 51 | default: 52 | return state; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/reducers/hospitalsReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/hospitals' 2 | 3 | const initialState = { 4 | fetching: false, 5 | count: 0, 6 | results: [], 7 | single: {} 8 | }; 9 | 10 | export default function hospitals(state = initialState, action) { 11 | switch (action.type) { 12 | case Actions.FETCH_HOSPITALS: 13 | return { 14 | ...state, 15 | fetching: true 16 | }; 17 | 18 | case Actions.SUCCESS_FETCH_HOSPITALS: 19 | return { 20 | ...state, 21 | fetching: false, 22 | count: action.data.count, 23 | results: action.data 24 | }; 25 | 26 | case Actions.FAILURE_FETCH_HOSPITALS: 27 | return { 28 | ...state, 29 | fetching: false 30 | }; 31 | 32 | case Actions.FETCH_HOSPITAL: 33 | return { 34 | ...state, 35 | fetching: true 36 | }; 37 | 38 | case Actions.SUCCESS_FETCH_HOSPITAL: 39 | return { 40 | ...state, 41 | fetching: false, 42 | single: action.data 43 | }; 44 | 45 | case Actions.FAILURE_FETCH_HOSPITAL: 46 | return { 47 | ...state, 48 | fetching: false 49 | }; 50 | 51 | default: 52 | return state; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0039_auto_20200418_1754.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-18 17:54 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0038_add_hospital_locations'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='donationdetail', 15 | name='price_per_piece', 16 | ), 17 | migrations.AddField( 18 | model_name='donationdetail', 19 | name='total_cost', 20 | field=models.DecimalField(decimal_places=2, default=0.0, help_text='Цена в сомах', max_digits=20, verbose_name='Общая стоимость'), 21 | ), 22 | migrations.AlterField( 23 | model_name='donation', 24 | name='donator_type', 25 | field=models.CharField(choices=[('ORGANIZATION', 'ORGANIZATION'), ('PERSONAL', 'PERSONAL'), ('DONOR', 'DONOR'), ('GOVERNMENT', 'GOVERNMENT')], default='ORGANIZATION', help_text='Выберите тип пожертвования', max_length=12, verbose_name='Тип пожертвования'), 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /frontend/components/layout/Layout.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Head from 'next/head'; 3 | import Header from './Header'; 4 | import { initGA, logPageView } from '../../utils/ga' 5 | 6 | class Layout extends React.Component { 7 | 8 | constructor(props) { 9 | super(props); 10 | } 11 | 12 | componentDidMount () { 13 | if (!window.GA_INITIALIZED) { 14 | initGA() 15 | window.GA_INITIALIZED = true 16 | } 17 | logPageView() 18 | } 19 | 20 | updateFilterValues(regionId, districtId, localityId) { 21 | this.props.onFilterValuesChange(regionId, districtId, localityId); 22 | } 23 | 24 | render() { 25 | const {children} = this.props; 26 | 27 | return ( 28 |
    29 | 30 | {'Тирек'} 31 | 32 | 33 |
    34 | {children} 35 |
    36 | ); 37 | } 38 | } 39 | 40 | export default Layout; 41 | -------------------------------------------------------------------------------- /frontend/actions/creators/contacts.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../contacts'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | 5 | const {publicRuntimeConfig} = getConfig(); 6 | 7 | export const fetchContacts = () => async dispatch => { 8 | dispatch({type: Actions.FETCH_CONTACTS}); 9 | try { 10 | const {data} = await axios.get(`${publicRuntimeConfig.apiUrl}/contact-info/`); 11 | return Promise.resolve(dispatch({type: Actions.SUCCESS_FETCH_CONTACTS, data})); 12 | } catch (err) { 13 | return Promise.resolve(dispatch({type: Actions.FAILURE_FETCH_CONTACTS})); 14 | } 15 | }; 16 | 17 | export const sendMessage = (_data) => async dispatch => { 18 | dispatch({type: Actions.SEND_MESSAGE}); 19 | try { 20 | await axios.post(`${publicRuntimeConfig.apiUrl}/contact-messages/`, _data); 21 | return Promise.resolve(dispatch({type: Actions.SUCCESS_SEND_MESSAGE})); 22 | } catch (err) { 23 | return Promise.resolve(dispatch({type: Actions.FAILURE_SEND_MESSAGE})); 24 | } 25 | }; 26 | 27 | export const init = () => async dispatch => { 28 | dispatch({type: Actions.INIT_CONTACT_FORM}); 29 | }; 30 | -------------------------------------------------------------------------------- /mobile/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:tirek_mobile/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new TirekApplication()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0002_auto_20200325_1805.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 18:05 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Measure', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=200, unique=True, verbose_name='Name (liters, kg, etc.)')), 19 | ], 20 | ), 21 | migrations.RemoveField( 22 | model_name='need', 23 | name='type', 24 | ), 25 | migrations.DeleteModel( 26 | name='NeedType', 27 | ), 28 | migrations.AddField( 29 | model_name='need', 30 | name='measure', 31 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='distributor.Measure', verbose_name='Measure (liters, kg, etc.)'), 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0009_auto_20200326_1932.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-26 19:32 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0008_auto_20200325_2328'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='hospital', 16 | name='phone_number', 17 | ), 18 | migrations.AddField( 19 | model_name='hospital', 20 | name='address', 21 | field=models.CharField(max_length=500, null=True, verbose_name='Address'), 22 | ), 23 | migrations.CreateModel( 24 | name='HospitalPhoneNumber', 25 | fields=[ 26 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 27 | ('value', models.CharField(max_length=30, verbose_name='Phone Number')), 28 | ('hospital', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='distributor.Hospital', verbose_name='Hospital')), 29 | ], 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /frontend/styles/_reset.scss: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | /* HTML5 display-role reset for older browsers */ 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | body { 27 | line-height: 1; 28 | font-family: 'Roboto', sans-serif; 29 | } 30 | ol, ul { 31 | list-style: none; 32 | } 33 | blockquote, q { 34 | quotes: none; 35 | } 36 | blockquote:before, blockquote:after, 37 | q:before, q:after { 38 | content: ''; 39 | content: none; 40 | } 41 | table { 42 | border-collapse: collapse; 43 | border-spacing: 0; 44 | } -------------------------------------------------------------------------------- /backend/distributor/migrations/0035_import_hospitals_addresses.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | 4 | from django.db import migrations 5 | 6 | 7 | def clean_row(row): 8 | for index, value in enumerate(row): 9 | if value: 10 | row[index] = value.strip() 11 | return row 12 | 13 | 14 | def import_hospitals_addresses(apps, schema_editor): 15 | Hospital = apps.get_model('distributor', 'Hospital') 16 | 17 | file_path = os.path.dirname(os.path.realpath(__file__)) + '/files/20200402-hospital-addresses.csv' 18 | with open(file_path, newline='') as file: 19 | csv_reader = csv.reader(file, delimiter=',', quotechar="'") 20 | next(csv_reader) 21 | for row in csv_reader: 22 | code, _, _, address = clean_row(row) 23 | if address and code: 24 | hospital = Hospital.objects.filter(code=code).first() 25 | if hospital: 26 | hospital.address = address 27 | hospital.save() 28 | 29 | 30 | class Migration(migrations.Migration): 31 | dependencies = [ 32 | ('distributor', '0034_auto_20200402_1233'), 33 | ] 34 | 35 | operations = [ 36 | migrations.RunPython(import_hospitals_addresses), 37 | ] 38 | -------------------------------------------------------------------------------- /frontend/actions/creators/districts.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../districts'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | 5 | const {publicRuntimeConfig} = getConfig(); 6 | 7 | export const fetchDistricts = (regionId) => async dispatch => { 8 | const params = {}; 9 | if (regionId) { 10 | params['region'] = regionId; 11 | } 12 | dispatch({type: Actions.FETCH_DISTRICTS}); 13 | try { 14 | const {data} = await axios.get(`${publicRuntimeConfig.apiUrl}/districts/`, { 15 | params: params 16 | }); 17 | return Promise.resolve(dispatch({type: Actions.SUCCESS_FETCH_DISTRICTS, data})); 18 | } catch (err) { 19 | return Promise.resolve(dispatch({type: Actions.FAILURE_FETCH_DISTRICTS})); 20 | } 21 | }; 22 | 23 | export const fetchDistrict = (districtId) => async dispatch => { 24 | dispatch({type: Actions.FETCH_DISTRICT}); 25 | try { 26 | const {data} = await axios.get(`${publicRuntimeConfig.apiUrl}/districts/${districtId}/`); 27 | return Promise.resolve(dispatch({type: Actions.SUCCESS_FETCH_DISTRICT, data})); 28 | } catch (err) { 29 | return Promise.resolve(dispatch({type: Actions.FAILURE_FETCH_DISTRICT})); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /frontend/actions/creators/hospitals.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../hospitals'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | const { publicRuntimeConfig } = getConfig(); 5 | 6 | export const fetchHospitals = (regionId, districtId, localityId, search) => async dispatch => { 7 | dispatch({ type: Actions.FETCH_HOSPITALS }); 8 | try { 9 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/all_hospitals/`, { 10 | params: { 11 | search_region_id: regionId, 12 | search_district_id: districtId, 13 | search_locality_id: localityId, 14 | search 15 | } 16 | }); 17 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_HOSPITALS, data })); 18 | } catch (err) { 19 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_HOSPITALS })); 20 | } 21 | }; 22 | export const fetchHospital = (hospitalId) => async dispatch => { 23 | dispatch({ type: Actions.FETCH_HOSPITAL }); 24 | try { 25 | const { data } = await axios.get(`${publicRuntimeConfig.apiUrl}/hospitals/${hospitalId}/`); 26 | return Promise.resolve(dispatch({ type: Actions.SUCCESS_FETCH_HOSPITAL, data })); 27 | } catch (err) { 28 | return Promise.resolve(dispatch({ type: Actions.FAILURE_FETCH_HOSPITAL })); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /backend/templates/admin/base_site.html: -------------------------------------------------------------------------------- 1 | {% extends "admin/base.html" %} 2 | {% load i18n %} 3 | {% load tags %} 4 | {% load static %} 5 | 6 | {% block extrastyle %} 7 | {{ block.super }} 8 | 9 | {% endblock %} 10 | 11 | {% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %} 12 | {% block extrahead %} 13 | 28 | {% endblock %} 29 | 30 | {% block branding %} 31 |

    {{ site_header|default:_('Django administration') }}

    32 |
    33 | {% trans "version" %} {% current_application_version %} 34 |
    35 | {% endblock %} 36 | 37 | {% block nav-global %}{% endblock %} -------------------------------------------------------------------------------- /mobile/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "503370235857", 4 | "firebase_url": "https://flutter-login-demo-5f414.firebaseio.com", 5 | "project_id": "flutter-login-demo-5f414", 6 | "storage_bucket": "flutter-login-demo-5f414.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:503370235857:android:253e1da980d40be4", 12 | "android_client_info": { 13 | "package_name": "com.example.flutterlogindemo" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "503370235857-3cuorpuf59haleofguql991tiv51db1b.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyDIOybdmyaxGXA7Mp7sx7y905lBMTIQfCQ" 25 | } 26 | ], 27 | "services": { 28 | "analytics_service": { 29 | "status": 1 30 | }, 31 | "appinvite_service": { 32 | "status": 1, 33 | "other_platform_oauth_client": [] 34 | }, 35 | "ads_service": { 36 | "status": 2 37 | } 38 | } 39 | } 40 | ], 41 | "configuration_version": "1" 42 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | 4 | backend: 5 | build: 6 | context: backend 7 | container_name: backend 8 | ports: 9 | - 8000:8000 10 | depends_on: 11 | - distributor-db 12 | - redis 13 | links: 14 | - distributor-db 15 | - redis 16 | env_file: 17 | - ./backend/.env 18 | environment: 19 | - DISTRIBUTOR_DB_HOST=distributor-db 20 | - DISTRIBUTOR_DB_NAME=distributor 21 | - DISTRIBUTOR_DB_USER=master 22 | - DISTRIBUTOR_DB_PASSWORD=123456 23 | - DISTRIBUTOR_DB_PORT=5432 24 | - REDIS_HOST=redis 25 | - REDIS_PORT=6379 26 | 27 | redis: 28 | image: redis:alpine 29 | container_name: redis 30 | restart: always 31 | expose: 32 | - 6379 33 | ports: 34 | - 6379:6379 35 | 36 | distributor-db: 37 | image: mdillon/postgis:9.5 38 | container_name: distributor-db 39 | hostname: distributor-db 40 | environment: 41 | - POSTGRES_USER=master 42 | - POSTGRES_PASSWORD=123456 43 | - POSTGRES_DB=distributor 44 | ports: 45 | - 5435:5432 46 | volumes: 47 | - distributor-postgres-data:/var/lib/postgresql/data 48 | 49 | volumes: 50 | distributor-postgres-data: 51 | driver: local 52 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0019_auto_20200328_0130.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-26 19:32 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0018_auto_20200327_2329'), 11 | ] 12 | 13 | operations = [ 14 | 15 | 16 | migrations.CreateModel( 17 | name='HospitalNeeds', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('reserve_amount', models.IntegerField(verbose_name='Reserve amount')), 21 | ('request_amount', models.IntegerField(verbose_name='Request amount')), 22 | ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created Date')), 23 | ('hospital', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='distributor.Hospital', verbose_name='Hospital')), 24 | ('need_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='distributor.NeedType', 25 | verbose_name='Need Type')), 26 | ], 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0053_fix_distribution_details.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-23 06:26 2 | 3 | from django.db import migrations, models, transaction 4 | import django.db.models.deletion 5 | 6 | def trunc_distributions(apps, schema_editor): 7 | with transaction.atomic(): 8 | Distribution = apps.get_model('distributor', 'Distribution') 9 | Distribution.objects.all().delete() 10 | 11 | class Migration(migrations.Migration): 12 | atomic = False 13 | 14 | dependencies = [ 15 | ('distributor', '0052_auto_20200422_2007'), 16 | ] 17 | 18 | operations = [ 19 | migrations.RunPython(trunc_distributions), 20 | migrations.AddField( 21 | model_name='distribution', 22 | name='donation', 23 | field=models.ForeignKey(help_text='Выберите ранее созданное пожертвование', 24 | on_delete=django.db.models.deletion.PROTECT, related_name='donation_details', 25 | to='distributor.Donation', verbose_name='Пожертвование)'), 26 | preserve_default=False, 27 | ), 28 | migrations.RemoveField( 29 | model_name='distributiondetail', 30 | name='donation', 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /frontend/actions/creators/localities.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../localities'; 2 | import axios from 'axios/index'; 3 | import getConfig from 'next/config'; 4 | 5 | const {publicRuntimeConfig} = getConfig(); 6 | 7 | export const fetchLocalities = (districtId) => async dispatch => { 8 | dispatch({type: Actions.FETCH_LOCALITIES}); 9 | try { 10 | const params = {}; 11 | if (districtId) { 12 | params['district'] = districtId; 13 | } 14 | const {data} = await axios.get(`${publicRuntimeConfig.apiUrl}/localities/`, { 15 | params: params 16 | }); 17 | return Promise.resolve(dispatch({type: Actions.SUCCESS_FETCH_LOCALITIES, data})); 18 | } catch (err) { 19 | return Promise.resolve(dispatch({type: Actions.FAILURE_FETCH_LOCALITIES})); 20 | } 21 | }; 22 | 23 | export const fetchLocality = (localityId) => async dispatch => { 24 | dispatch({type: Actions.FETCH_LOCALITY}); 25 | try { 26 | const {data} = await axios.get(`${publicRuntimeConfig.apiUrl}/localities/${localityId}/`); 27 | return Promise.resolve(dispatch({type: Actions.SUCCESS_FETCH_LOCALITY, data})); 28 | } catch (err) { 29 | return Promise.resolve(dispatch({type: Actions.FAILURE_FETCH_LOCALITY})); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /backend/backend/urls.py: -------------------------------------------------------------------------------- 1 | """backend URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.0/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | from rest_framework.documentation import include_docs_urls 20 | 21 | v1 = ([ 22 | path('', include('distributor.urls')), 23 | path('', include('users.urls')), 24 | ], 'v1') 25 | 26 | urlpatterns = [ 27 | url(r'^jet/', include('jet.urls', 'jet')), 28 | path('admin/', admin.site.urls), 29 | path('api/v1/', include(v1)), 30 | url(r'^api-auth/', include('rest_framework.urls')), 31 | url(r'^docs/', include_docs_urls(title='API Docs', public=False)), 32 | ] 33 | -------------------------------------------------------------------------------- /backend/DEPLOY.md: -------------------------------------------------------------------------------- 1 | ### How to deploy 2 | 3 | Copy .env.dev to .env file, then edit: 4 | ``` 5 | SECRET_KEY=put something weird here 6 | DJANGO_SETTINGS_MODULE=backend.production 7 | STATIC_ROOT = '/var/www/covid-supply/static' 8 | ``` 9 | 10 | Run commands: 11 | ``` 12 | # copy static files 13 | python manage.py collectstatic 14 | # run migrations 15 | python manage.py migrate 16 | # restart gunicorn 17 | systemctl restart gunicorn 18 | ``` 19 | 20 | Gunicorn systemd file, note the `!SET_PORT_HERE!` thingie: 21 | ``` 22 | [Unit] 23 | Description=gunicorn daemon 24 | After=network.target 25 | 26 | [Service] 27 | Type=simple 28 | # the specific user that our service will run as 29 | User=www-data 30 | Group=www-data 31 | # another option for an even more restricted service is 32 | # DynamicUser=yes 33 | # see http://0pointer.net/blog/dynamic-users-with-systemd.html 34 | WorkingDirectory=/opt/covid-supply/backend 35 | ExecStart=/opt/covid-supply/backend/venv/bin/gunicorn -b 127.0.0.1:!SET_PORT_HERE! -w 3 backend.wsgi 36 | ExecReload=/bin/kill -s HUP $MAINPID 37 | KillMode=mixed 38 | TimeoutStopSec=5 39 | PrivateTmp=true 40 | 41 | [Install] 42 | WantedBy=multi-user.target 43 | ``` 44 | 45 | Create log dir & file: 46 | ``` 47 | mkdir -p /var/log/django 48 | touch /var/log/django/error.log 49 | chown -R www-data /var/log/django 50 | ``` 51 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following resources: 20 | 21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 22 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 23 | 24 | You can check out [the Next.js GitHub repository](https://github.com/zeit/next.js/) - your feedback and contributions are welcome! 25 | 26 | ## Deploy on ZEIT Now 27 | 28 | The easiest way to deploy your Next.js app is to use the [ZEIT Now Platform](https://zeit.co/import?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 29 | 30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 31 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0012_statistic.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-27 18:57 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0011_auto_20200326_2123'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Statistic', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=200, verbose_name='Name')), 19 | ('name_ru', models.CharField(max_length=200, null=True, verbose_name='Name')), 20 | ('name_ky', models.CharField(max_length=200, null=True, verbose_name='Name')), 21 | ('actual', models.PositiveSmallIntegerField(verbose_name='Actual')), 22 | ('capacity', models.PositiveSmallIntegerField(verbose_name='Capacity')), 23 | ('has_capacity', models.BooleanField(default=False, verbose_name='Has Capacity?')), 24 | ('hospital', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='statistics', to='distributor.Hospital', verbose_name='Hospital')), 25 | ], 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0021_page.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-28 21:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0020_auto_20200328_1131'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Page', 15 | fields=[ 16 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('name', models.CharField(max_length=200, verbose_name='Name')), 18 | ('name_ru', models.CharField(max_length=200, null=True, verbose_name='Name')), 19 | ('name_ky', models.CharField(max_length=200, null=True, verbose_name='Name')), 20 | ('url', models.CharField(max_length=200, verbose_name='Url')), 21 | ('content', models.TextField(max_length=1000, verbose_name='Content')), 22 | ('content_ru', models.TextField(max_length=1000, null=True, verbose_name='Content')), 23 | ('content_ky', models.TextField(max_length=1000, null=True, verbose_name='Content')), 24 | ], 25 | options={ 26 | 'verbose_name': 'Page', 27 | 'verbose_name_plural': 'Pages', 28 | }, 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /frontend/reducers/localitiesReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/localities' 2 | 3 | const initialState = { 4 | fetching: false, 5 | count: 0, 6 | results: [], 7 | single: {} 8 | }; 9 | 10 | export default function hospitals(state = initialState, action) { 11 | switch (action.type) { 12 | case Actions.FETCH_LOCALITIES: 13 | return { 14 | ...state, 15 | fetching: true 16 | }; 17 | 18 | case Actions.SUCCESS_FETCH_LOCALITIES: 19 | return { 20 | ...state, 21 | fetching: false, 22 | count: action.data.length, 23 | results: action.data 24 | }; 25 | 26 | case Actions.FAILURE_FETCH_LOCALITIES: 27 | return { 28 | ...state, 29 | fetching: false 30 | }; 31 | 32 | case Actions.FETCH_LOCALITY: 33 | return { 34 | ...state, 35 | fetching: true 36 | }; 37 | 38 | case Actions.SUCCESS_FETCH_LOCALITY: 39 | return { 40 | ...state, 41 | fetching: false, 42 | single: action.data 43 | }; 44 | 45 | case Actions.FAILURE_FETCH_LOCALITY: 46 | return { 47 | ...state, 48 | fetching: false 49 | }; 50 | 51 | default: 52 | return state; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/components/contacts/Info.jsx: -------------------------------------------------------------------------------- 1 | import React, {Fragment} from "react"; 2 | 3 | const Info = (props) => { 4 | const {data} = props; 5 | const whatsAppFormat = (number) => { 6 | const oldNumber = number.replace(/\s+/g, '').replace(/-/g, '') 7 | const newNumber = oldNumber.indexOf('0') == 0 ? '996'+oldNumber.substring(1) : oldNumber; 8 | return newNumber 9 | }; 10 | 11 | return ( 12 | 13 |

    Контакты

    14 |

    15 | {data.text_ru}: 16 |

    17 |
      18 | { 19 | data.phone_numbers && data.phone_numbers.map(number => 20 |
    • 21 | {number.value} 22 | {number.is_whats_app && } 23 |
    • 24 | ) 25 | } 26 |
    27 | 28 |

    29 | Email: { 30 | data.emails && data.emails.map(email => 31 | 32 | {email.value} 33 |   34 | 35 | ) 36 | } 37 |

    38 |
    39 | ) 40 | }; 41 | 42 | export default Info 43 | -------------------------------------------------------------------------------- /mobile/tirek_mobile_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /mobile/ios/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AD_UNIT_ID_FOR_BANNER_TEST 6 | ca-app-pub-3940256099942544/2934735716 7 | AD_UNIT_ID_FOR_INTERSTITIAL_TEST 8 | ca-app-pub-3940256099942544/4411468910 9 | CLIENT_ID 10 | 503370235857-sh78fhp4s2r2qtqaajb1m32pkfd0l1s9.apps.googleusercontent.com 11 | REVERSED_CLIENT_ID 12 | com.googleusercontent.apps.503370235857-sh78fhp4s2r2qtqaajb1m32pkfd0l1s9 13 | API_KEY 14 | AIzaSyDQcM5m7eR7kVZ2Rl_EQOC-eXQ_h3KYWtc 15 | GCM_SENDER_ID 16 | 503370235857 17 | PLIST_VERSION 18 | 1 19 | BUNDLE_ID 20 | com.example.flutterLoginDemo 21 | PROJECT_ID 22 | flutter-login-demo-5f414 23 | STORAGE_BUCKET 24 | flutter-login-demo-5f414.appspot.com 25 | IS_ADS_ENABLED 26 | 27 | IS_ANALYTICS_ENABLED 28 | 29 | IS_APPINVITE_ENABLED 30 | 31 | IS_GCM_ENABLED 32 | 33 | IS_SIGNIN_ENABLED 34 | 35 | GOOGLE_APP_ID 36 | 1:503370235857:ios:fae8bbf356b4bb88 37 | DATABASE_URL 38 | https://flutter-login-demo-5f414.firebaseio.com 39 | 40 | -------------------------------------------------------------------------------- /mobile/ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AD_UNIT_ID_FOR_BANNER_TEST 6 | ca-app-pub-3940256099942544/2934735716 7 | AD_UNIT_ID_FOR_INTERSTITIAL_TEST 8 | ca-app-pub-3940256099942544/4411468910 9 | CLIENT_ID 10 | 503370235857-sh78fhp4s2r2qtqaajb1m32pkfd0l1s9.apps.googleusercontent.com 11 | REVERSED_CLIENT_ID 12 | com.googleusercontent.apps.503370235857-sh78fhp4s2r2qtqaajb1m32pkfd0l1s9 13 | API_KEY 14 | AIzaSyDQcM5m7eR7kVZ2Rl_EQOC-eXQ_h3KYWtc 15 | GCM_SENDER_ID 16 | 503370235857 17 | PLIST_VERSION 18 | 1 19 | BUNDLE_ID 20 | com.example.flutterLoginDemo 21 | PROJECT_ID 22 | flutter-login-demo-5f414 23 | STORAGE_BUCKET 24 | flutter-login-demo-5f414.appspot.com 25 | IS_ADS_ENABLED 26 | 27 | IS_ANALYTICS_ENABLED 28 | 29 | IS_APPINVITE_ENABLED 30 | 31 | IS_GCM_ENABLED 32 | 33 | IS_SIGNIN_ENABLED 34 | 35 | GOOGLE_APP_ID 36 | 1:503370235857:ios:fae8bbf356b4bb88 37 | DATABASE_URL 38 | https://flutter-login-demo-5f414.firebaseio.com 39 | 40 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0027_auto_20200330_1631.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-30 16:31 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0026_merge_20200329_1841'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='donationdetail', 15 | name='amount', 16 | field=models.PositiveIntegerField(help_text='Введите количество', verbose_name='Количество'), 17 | ), 18 | migrations.AlterField( 19 | model_name='donationdetail', 20 | name='price_per_piece', 21 | field=models.DecimalField(decimal_places=2, default=1, help_text='Цена в сомах', max_digits=12, verbose_name='Цена за одну единицу (KGS)'), 22 | preserve_default=False, 23 | ), 24 | migrations.AlterField( 25 | model_name='statistic', 26 | name='actual', 27 | field=models.IntegerField(verbose_name='Текущий показатель'), 28 | ), 29 | migrations.AlterField( 30 | model_name='statistic', 31 | name='capacity', 32 | field=models.IntegerField(blank=True, null=True, verbose_name='Требуемое количество'), 33 | ), 34 | migrations.AlterField( 35 | model_name='statistic', 36 | name='has_capacity', 37 | field=models.BooleanField(default=False, help_text='Отметьте галочкой чтобы показать поле', verbose_name='Показывать поле "Требуемое количество" '), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /mobile/lib/helper/ApiHelper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:http/http.dart' as http; 5 | import 'package:tirek_mobile/exception/TirekException.dart'; 6 | 7 | class ApiHelper { 8 | static final String baseUrl = "https://antivirus.el.kg/api/v1/"; 9 | 10 | static Future post( 11 | String path, Map headers, String body) async { 12 | try { 13 | final response = 14 | await http.post(baseUrl + path, headers: headers, body: body); 15 | 16 | return _returnResponse(response); 17 | } on SocketException { 18 | throw FetchDataException('Нет подключения к интернету'); 19 | } 20 | } 21 | 22 | static Future get(String path, Map headers) async { 23 | try { 24 | final response = await http.get(baseUrl + path, headers: headers); 25 | 26 | return _returnResponse(response); 27 | } on SocketException { 28 | throw FetchDataException('Нет подключения к интернету'); 29 | } 30 | } 31 | 32 | static dynamic _returnResponse(http.Response response) { 33 | switch (response.statusCode) { 34 | case 200: 35 | case 201: 36 | var responseJson = json.decode(utf8.decode(response.bodyBytes)); 37 | return responseJson; 38 | case 400: 39 | throw BadRequestException(response.body.toString()); 40 | case 401: 41 | case 403: 42 | throw UnauthorisedException(response.body.toString()); 43 | case 500: 44 | default: 45 | throw FetchDataException('Произошла ошибка : ${response.statusCode}'); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0039_distribution.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-05 16:14 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0038_add_hospital_locations'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Distribution', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('sender', models.TextField(verbose_name='Кто выдал?')), 19 | ('receiver', models.TextField(verbose_name='Кто принял?')), 20 | ('date_of_distribute', models.DateField(verbose_name='Дата распределения')), 21 | ('status', models.CharField(choices=[('ready_to_sent', 'Подготовлено'), ('sent', 'Отправлено'), ('delivered', 'Доставлено')], max_length=20, verbose_name='Статус')), 22 | ('created_at', models.DateTimeField(auto_now_add=True)), 23 | ('updated_at', models.DateTimeField(auto_now=True)), 24 | ('donations', models.ManyToManyField(to='distributor.Donation', verbose_name='Пожертвования')), 25 | ('hospital', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='distributions', to='distributor.Hospital')), 26 | ], 27 | options={ 28 | 'verbose_name': 'Запись для распределения', 29 | 'verbose_name_plural': 'Распределения', 30 | }, 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /frontend/components/distributions/distribution.js: -------------------------------------------------------------------------------- 1 | import {Card, Table} from "react-bootstrap"; 2 | import React, {Fragment} from "react"; 3 | 4 | const Distribution = (props) => { 5 | const item = props.item; 6 | const details = (item.details || []).map(detail => { 7 | const key = `${item.id} ${detail.id}` 8 | return ( 9 | 10 | 11 | 12 | {detail.need_type.name} 13 | 14 | 15 | 16 | 17 | {detail.amount} {detail.need_type.measure.name} 18 | 19 | 20 | 21 | ) 22 | }); 23 | 24 | return ( 25 | 26 | 27 | 28 | {item.hospital.name} 29 | 30 | 31 | Получено: {item.receiver} {item.distributed_at} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {details} 43 | 44 |
    НаименованиеКол-во
    45 |
    46 |
    47 | ) 48 | } 49 | 50 | export default Distribution; -------------------------------------------------------------------------------- /backend/distributor/migrations/0008_auto_20200325_2328.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 23:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('distributor', '0007_auto_20200325_2322'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='donation', 15 | name='description_ky', 16 | field=models.TextField(max_length=1000, null=True, verbose_name='Donation Description'), 17 | ), 18 | migrations.AddField( 19 | model_name='donation', 20 | name='description_ru', 21 | field=models.TextField(max_length=1000, null=True, verbose_name='Donation Description'), 22 | ), 23 | migrations.AddField( 24 | model_name='donation', 25 | name='donator_name_ky', 26 | field=models.CharField(max_length=200, null=True, verbose_name='Donator Name'), 27 | ), 28 | migrations.AddField( 29 | model_name='donation', 30 | name='donator_name_ru', 31 | field=models.CharField(max_length=200, null=True, verbose_name='Donator Name'), 32 | ), 33 | migrations.AddField( 34 | model_name='hospital', 35 | name='name_ky', 36 | field=models.CharField(max_length=200, null=True, verbose_name='Name'), 37 | ), 38 | migrations.AddField( 39 | model_name='hospital', 40 | name='name_ru', 41 | field=models.CharField(max_length=200, null=True, verbose_name='Name'), 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0017_helprequest.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-27 22:48 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0016_auto_20200327_2112'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='HelpRequest', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('first_name', models.CharField(max_length=50, verbose_name='First Name')), 19 | ('last_name', models.CharField(max_length=50, verbose_name='Last Name')), 20 | ('position', models.CharField(max_length=50, verbose_name='Position')), 21 | ('hospital_name', models.CharField(max_length=250, verbose_name='Hospital Name')), 22 | ('phone_number', models.CharField(max_length=100, verbose_name='Phone Number')), 23 | ('description', models.TextField(max_length=500, verbose_name='Description')), 24 | ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created Date')), 25 | ('is_read', models.BooleanField(default=False, verbose_name='Read')), 26 | ('read_at', models.DateTimeField(blank=True, editable=False, null=True, verbose_name='Read Date')), 27 | ('locality', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='distributor.Locality', verbose_name='Locality')), 28 | ], 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /mobile/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | tirek_mobile 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /frontend/reducers/contactsReducer.js: -------------------------------------------------------------------------------- 1 | import * as Actions from '../actions/contacts' 2 | import * as status from '../constants/messageStatus' 3 | 4 | const initialState = { 5 | sending: false, 6 | status: status.INIT, 7 | fetching: false, 8 | data: {} 9 | }; 10 | 11 | export default function districts(state = initialState, action) { 12 | switch (action.type) { 13 | case Actions.FETCH_CONTACTS: 14 | return { 15 | ...state, 16 | fetching: true 17 | }; 18 | 19 | case Actions.SUCCESS_FETCH_CONTACTS: 20 | return { 21 | ...state, 22 | fetching: false, 23 | data: action.data 24 | }; 25 | 26 | case Actions.FAILURE_FETCH_CONTACTS: 27 | return { 28 | ...state, 29 | fetching: false 30 | }; 31 | 32 | case Actions.SEND_MESSAGE: 33 | return { 34 | ...state, 35 | sending: true 36 | }; 37 | 38 | case Actions.SUCCESS_SEND_MESSAGE: 39 | return { 40 | ...state, 41 | sending: false, 42 | status: status.SUCCESS 43 | }; 44 | 45 | case Actions.FAILURE_SEND_MESSAGE: 46 | return { 47 | ...state, 48 | sending: false, 49 | status: status.FAILURE 50 | }; 51 | 52 | case Actions.INIT_CONTACT_FORM: 53 | return { 54 | ...state, 55 | status: status.INIT 56 | }; 57 | 58 | default: 59 | return state; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/flutter 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=flutter 4 | 5 | ### Flutter ### 6 | # Flutter/Dart/Pub related 7 | **/doc/api/ 8 | .dart_tool/ 9 | .flutter-plugins 10 | .flutter-plugins-dependencies 11 | .packages 12 | .pub-cache/ 13 | .pub/ 14 | build/ 15 | 16 | # Android related 17 | **/android/**/gradle-wrapper.jar 18 | **/android/.gradle 19 | **/android/captures/ 20 | **/android/gradlew 21 | **/android/gradlew.bat 22 | **/android/local.properties 23 | **/android/**/GeneratedPluginRegistrant.java 24 | 25 | # iOS/XCode related 26 | **/ios/**/*.mode1v3 27 | **/ios/**/*.mode2v3 28 | **/ios/**/*.moved-aside 29 | **/ios/**/*.pbxuser 30 | **/ios/**/*.perspectivev3 31 | **/ios/**/*sync/ 32 | **/ios/**/.sconsign.dblite 33 | **/ios/**/.tags* 34 | **/ios/**/.vagrant/ 35 | **/ios/**/DerivedData/ 36 | **/ios/**/Icon? 37 | **/ios/**/Pods/ 38 | **/ios/**/.symlinks/ 39 | **/ios/**/profile 40 | **/ios/**/xcuserdata 41 | **/ios/.generated/ 42 | **/ios/Flutter/App.framework 43 | **/ios/Flutter/Flutter.framework 44 | **/ios/Flutter/Flutter.podspec 45 | **/ios/Flutter/Generated.xcconfig 46 | **/ios/Flutter/app.flx 47 | **/ios/Flutter/app.zip 48 | **/ios/Flutter/flutter_assets/ 49 | **/ios/Flutter/flutter_export_environment.sh 50 | **/ios/ServiceDefinitions.json 51 | **/ios/Runner/GeneratedPluginRegistrant.* 52 | 53 | # Exceptions to above rules. 54 | !**/ios/**/default.mode1v3 55 | !**/ios/**/default.mode2v3 56 | !**/ios/**/default.pbxuser 57 | !**/ios/**/default.perspectivev3 58 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 59 | 60 | # End of https://www.toptal.com/developers/gitignore/api/flutter -------------------------------------------------------------------------------- /.github/workflows/django.yml: -------------------------------------------------------------------------------- 1 | # source - https://hacksoft.blog/github-actions-in-action-setting-up-django-and-postgres/ 2 | name: Python application 3 | on: 4 | push: 5 | paths: 6 | - 'backend/*' 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | services: 11 | distributor-db: 12 | image: mdillon/postgis:9.5 13 | env: 14 | POSTGRES_USER: master 15 | POSTGRES_PASSWORD: 123456 16 | POSTGRES_DB: distributor 17 | ports: 18 | - 5432:5432 19 | # needed because the postgres container does not provide a healthcheck 20 | options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 21 | redis: 22 | image: redis:alpine 23 | ports: 24 | - 6379:6379 25 | steps: 26 | - name: Checkout tha sauce 27 | uses: actions/checkout@v1 28 | 29 | - name: Set up Python 3.7 30 | uses: actions/setup-python@v1 31 | with: 32 | python-version: 3.7 33 | 34 | - name: psycopg2 prerequisites 35 | run: sudo apt-get install python-dev libpq-dev binutils libproj-dev gdal-bin 36 | 37 | - name: Install dependencies 38 | run: | 39 | python -m pip install --upgrade pip 40 | pip install -r requirements.txt 41 | working-directory: backend 42 | 43 | - name: Prepare dotenv 44 | run: cp .env.github .env 45 | working-directory: backend 46 | 47 | - name: Run migrations 48 | run: python manage.py migrate 49 | working-directory: backend 50 | 51 | - name: Run tests 52 | run: python manage.py test 53 | working-directory: backend 54 | -------------------------------------------------------------------------------- /mobile/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0044_auto_20200420_1853.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 18:53 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0043_auto_20200405_1813'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RemoveField( 15 | model_name='distribution', 16 | name='donations', 17 | ), 18 | migrations.CreateModel( 19 | name='DistributionDetail', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('amount', models.PositiveIntegerField(help_text='Введите количество', verbose_name='Количество')), 23 | ('price_per_piece', models.DecimalField(decimal_places=2, help_text='Цена в сомах', max_digits=12, verbose_name='Цена за одну единицу (KGS)')), 24 | ('distribution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='distributor.Distribution')), 25 | ('donation', models.ForeignKey(help_text='Выберите ранее созданное пожертвование', on_delete=django.db.models.deletion.PROTECT, related_name='distribution_details', to='distributor.Donation', verbose_name='Пожертвование)')), 26 | ('need_type', models.ForeignKey(help_text='Выберите тип нужды', on_delete=django.db.models.deletion.PROTECT, to='distributor.NeedType', verbose_name='Тип нужды')), 27 | ], 28 | options={ 29 | 'verbose_name': 'Детали распределния', 30 | 'verbose_name_plural': 'Детали распределений', 31 | }, 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /mobile/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | apply plugin: 'com.android.application' 15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 16 | 17 | android { 18 | compileSdkVersion 28 19 | 20 | lintOptions { 21 | disable 'InvalidPackage' 22 | } 23 | 24 | defaultConfig { 25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 26 | applicationId "com.example.flutterlogindemo" 27 | minSdkVersion 16 28 | targetSdkVersion 28 29 | versionCode 1 30 | versionName "1.0" 31 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 32 | } 33 | 34 | buildTypes { 35 | release { 36 | // TODO: Add your own signing config for the release build. 37 | // Signing with the debug keys for now, so `flutter run --release` works. 38 | signingConfig signingConfigs.debug 39 | } 40 | } 41 | } 42 | 43 | flutter { 44 | source '../..' 45 | } 46 | 47 | dependencies { 48 | testImplementation 'junit:junit:4.12' 49 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' 51 | } 52 | 53 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /backend/distributor/migrations/0003_auto_20200325_1806.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 18:06 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('distributor', '0002_auto_20200325_1805'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='NeedType', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('name', models.CharField(max_length=200, verbose_name='Name')), 21 | ('price_per_piece', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='Price per piece (KGS)')), 22 | ('modified_at', models.DateTimeField(auto_now=True, null=True, verbose_name='Modified Date')), 23 | ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created Date')), 24 | ('created_by', models.ForeignKey(auto_created=True, editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='needtype_created_by', to=settings.AUTH_USER_MODEL)), 25 | ('measure', models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='distributor.Measure', verbose_name='Measure (liters, kg, etc.)')), 26 | ('modified_by', models.ForeignKey(auto_created=True, editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='needtype_modified_by', to=settings.AUTH_USER_MODEL)), 27 | ], 28 | ), 29 | migrations.DeleteModel( 30 | name='Need', 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /frontend/config/with-redux-store.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { initializeStore } from './store' 3 | 4 | const isServer = typeof window === 'undefined' 5 | const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__' 6 | 7 | function getOrCreateStore (initialState) { 8 | // Always make a new store if server, otherwise state is shared between requests 9 | if (isServer) { 10 | return initializeStore(initialState) 11 | } 12 | 13 | // Create store if unavailable on the client and set it on the window object 14 | if (!window[__NEXT_REDUX_STORE__]) { 15 | window[__NEXT_REDUX_STORE__] = initializeStore(initialState) 16 | } 17 | return window[__NEXT_REDUX_STORE__] 18 | } 19 | 20 | export default App => { 21 | return class AppWithRedux extends React.Component { 22 | static async getInitialProps (appContext) { 23 | // Get or Create the store with `undefined` as initialState 24 | // This allows you to set a custom default initialState 25 | const reduxStore = getOrCreateStore() 26 | 27 | // Provide the store to getInitialProps of pages 28 | appContext.ctx.reduxStore = reduxStore 29 | 30 | let appProps = {} 31 | if (typeof App.getInitialProps === 'function') { 32 | appProps = await App.getInitialProps(appContext) 33 | } 34 | 35 | return { 36 | ...appProps, 37 | initialReduxState: reduxStore.getState() 38 | } 39 | } 40 | 41 | constructor (props) { 42 | super(props) 43 | this.reduxStore = getOrCreateStore(props.initialReduxState) 44 | } 45 | 46 | render () { 47 | return 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /mobile/lib/services/NeedsRequestService.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:tirek_mobile/helper/ApiHelper.dart'; 3 | 4 | import 'package:tirek_mobile/models/response/NeedsRequestResponse.dart'; 5 | import 'package:tirek_mobile/models/response/NeedsTypeResponse.dart'; 6 | import 'package:tirek_mobile/services/SharedPreferencesService.dart'; 7 | import 'package:tirek_mobile/models/response/HospitalResponse.dart'; 8 | import 'dart:convert'; 9 | 10 | abstract class NeedsRequestService { 11 | Future post(Hospital hospital, NeedsType needType, 12 | String reserveAmount, String requestAmount, String requestAmountMonth); 13 | } 14 | 15 | class TirekNeedsRequestService implements NeedsRequestService { 16 | final SharedPreferencesService sharedPreferencesService; 17 | 18 | TirekNeedsRequestService(this.sharedPreferencesService); 19 | 20 | @override 21 | Future post( 22 | Hospital hospital, 23 | NeedsType needType, 24 | String reserveAmount, 25 | String requestAmount, 26 | String requestAmountMonth) async { 27 | final userInfo = await sharedPreferencesService.getCurrentUserInfo(); 28 | 29 | final body = json.encode({ 30 | "hospital_id": hospital.id, 31 | "need_type_id": needType.id, 32 | "reserve_amount": reserveAmount, 33 | "request_amount": requestAmount, 34 | "request_amount_month": requestAmountMonth 35 | }); 36 | 37 | final Map headers = { 38 | 'Authorization': "Token ${userInfo.token}", 39 | 'Content-Type': 'application/json; charset=utf-8', 40 | 'Accept': 'application/json; charset=utf-8', 41 | }; 42 | 43 | final responseJson = await ApiHelper.post("hospital-needs/", headers, body); 44 | return NeedsRequestResponse.fromJson(responseJson); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontend/public/whatsapp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /mobile/lib/services/SharedPreferencesService.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | import 'package:tirek_mobile/models/response/AuthenticationResponse.dart'; 3 | import 'package:tirek_mobile/models/response/User.dart'; 4 | 5 | abstract class SharedPreferencesService { 6 | saveAuthenticationResponse(AuthenticationResponse authenticationResponse); 7 | 8 | Future getCurrentUserInfo(); 9 | 10 | isLoggedIn(); 11 | 12 | removeCurrentUserInfo(); 13 | } 14 | 15 | class TirekSharedPreferencesService implements SharedPreferencesService { 16 | @override 17 | saveAuthenticationResponse( 18 | AuthenticationResponse authenticationResponse) async { 19 | SharedPreferences prefs = await SharedPreferences.getInstance(); 20 | 21 | await prefs.setString('token', authenticationResponse.token); 22 | await prefs.setInt('userId', authenticationResponse.user.id); 23 | await prefs.setString('userFullName', authenticationResponse.user.fullName); 24 | } 25 | 26 | @override 27 | Future getCurrentUserInfo() async { 28 | SharedPreferences prefs = await SharedPreferences.getInstance(); 29 | final token = prefs.getString('token'); 30 | final userId = prefs.getInt('userId'); 31 | final fullName = prefs.getString('userFullName'); 32 | 33 | return AuthenticationResponse(token, User(userId, fullName)); 34 | } 35 | 36 | @override 37 | isLoggedIn() async { 38 | SharedPreferences prefs = await SharedPreferences.getInstance(); 39 | final token = prefs.getString('token'); 40 | return token != null; 41 | } 42 | 43 | @override 44 | removeCurrentUserInfo() async { 45 | SharedPreferences prefs = await SharedPreferences.getInstance(); 46 | await prefs.remove('token'); 47 | await prefs.remove('userId'); 48 | await prefs.remove('userFullName'); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0014_auto_20200327_2104.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-27 21:04 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('distributor', '0013_auto_20200327_2023'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='StatisticCategory', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=200, unique=True, verbose_name='Name')), 19 | ('name_ru', models.CharField(max_length=200, null=True, unique=True, verbose_name='Name')), 20 | ('name_ky', models.CharField(max_length=200, null=True, unique=True, verbose_name='Name')), 21 | ], 22 | options={ 23 | 'verbose_name': 'Statistic Category', 24 | 'verbose_name_plural': 'Statistic Categories', 25 | }, 26 | ), 27 | migrations.AlterModelOptions( 28 | name='needtype', 29 | options={'verbose_name': 'Need Type', 'verbose_name_plural': 'Need Types'}, 30 | ), 31 | migrations.RemoveField( 32 | model_name='statistic', 33 | name='name', 34 | ), 35 | migrations.RemoveField( 36 | model_name='statistic', 37 | name='name_ky', 38 | ), 39 | migrations.RemoveField( 40 | model_name='statistic', 41 | name='name_ru', 42 | ), 43 | migrations.AddField( 44 | model_name='statistic', 45 | name='category', 46 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='distributor.StatisticCategory', verbose_name='Category'), 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /frontend/components/contacts/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux'; 3 | import {bindActionCreators} from 'redux'; 4 | import { 5 | fetchContacts as fetchContactsAction, 6 | sendMessage as sendMessageAction, 7 | init as initAction 8 | } from '../../actions/creators/contacts'; 9 | import {Col, Container, Row} from 'react-bootstrap' 10 | import Info from "./Info"; 11 | import Message from "./Message"; 12 | 13 | class Contacts extends Component { 14 | componentDidMount() { 15 | this.props.fetchContactsAction(); 16 | this.props.initAction(); 17 | } 18 | 19 | render() { 20 | const {data, sending, status, sendMessageAction} = this.props; 21 | 22 | return ( 23 |
    24 | 25 | 26 | 27 | 28 | 29 | 30 | 35 | 36 | 37 | 38 |
    39 | ); 40 | } 41 | } 42 | 43 | 44 | const mapStateToProps = (state) => { 45 | return { 46 | sending: state.contacts.sending, 47 | status: state.contacts.status, 48 | data: state.contacts.data, 49 | } 50 | }; 51 | 52 | const mapDispatchToProps = (dispatch) => bindActionCreators({ 53 | fetchContactsAction, 54 | sendMessageAction, 55 | initAction 56 | }, dispatch); 57 | 58 | export default connect(mapStateToProps, mapDispatchToProps)(Contacts) 59 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 17:51 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='NeedType', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=200, unique=True, verbose_name='Name')), 22 | ], 23 | ), 24 | migrations.CreateModel( 25 | name='Need', 26 | fields=[ 27 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 28 | ('name', models.CharField(max_length=200, verbose_name='Name')), 29 | ('price_per_piece', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='Price per piece (KGS)')), 30 | ('modified_at', models.DateTimeField(auto_now=True, null=True, verbose_name='Modified Date')), 31 | ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created Date')), 32 | ('created_by', models.ForeignKey(auto_created=True, editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='need_created_by', to=settings.AUTH_USER_MODEL)), 33 | ('modified_by', models.ForeignKey(auto_created=True, editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='need_modified_by', to=settings.AUTH_USER_MODEL)), 34 | ('type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='distributor.NeedType', verbose_name='Type')), 35 | ], 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0051_add_manager_group.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-04-20 18:52 2 | from django.db import migrations 3 | from django.contrib.auth.management import create_permissions 4 | 5 | 6 | def add_group(apps, schema_editor): 7 | permissions = [ 8 | 'add_hospital', 9 | 'change_hospital', 10 | 'view_hospital', 11 | 12 | 'add_hospitalphonenumber', 13 | 'change_hospitalphonenumber', 14 | 'delete_hospitalphonenumber', 15 | 'view_hospitalphonenumber', 16 | 17 | 'add_hospitalneeds', 18 | 'change_hospitalneeds', 19 | 'delete_hospitalneeds', 20 | 'view_hospitalneeds', 21 | 22 | 'add_statistic', 23 | 'change_statistic', 24 | 'delete_statistic', 25 | 'view_statistic', 26 | 27 | 'add_distributiondetail', 28 | 'change_distributiondetail', 29 | 'delete_distributiondetail', 30 | 'view_distributiondetail', 31 | 32 | 'add_distribution', 33 | 'change_distribution', 34 | 'delete_distribution', 35 | 'view_distribution', 36 | ] 37 | 38 | # Костыль из https://stackoverflow.com/a/31735043/490502 39 | for app_config in apps.get_app_configs(): 40 | app_config.models_module = True 41 | create_permissions(app_config, apps=apps, verbosity=0) 42 | app_config.models_module = None 43 | 44 | group, created = apps.get_model('auth', 'Group').objects.get_or_create(name="Hospital Managers") 45 | if created: 46 | print(f'{group} Group created') 47 | for permission in permissions: 48 | group.permissions.add(apps.get_model('auth', 'Permission').objects.get(codename=permission)) 49 | print(f'Permitting {group} to {permission}') 50 | 51 | 52 | class Migration(migrations.Migration): 53 | dependencies = [ 54 | ('distributor', '0050_auto_20200420_1852'), 55 | ] 56 | 57 | operations = [ 58 | migrations.RunPython(add_group) 59 | ] 60 | -------------------------------------------------------------------------------- /backend/distributor/translation.py: -------------------------------------------------------------------------------- 1 | from modeltranslation.translator import translator, TranslationOptions 2 | 3 | from .models import ( 4 | Measure, NeedType, Donation, 5 | Hospital, Region, District, 6 | Locality, StatisticCategory, Page, ContactInfo, ContactMessage) 7 | 8 | 9 | class MeasureTranslationOptions(TranslationOptions): 10 | fields = ('name',) 11 | 12 | 13 | translator.register(Measure, MeasureTranslationOptions) 14 | 15 | 16 | class NeedTypeTranslationOptions(TranslationOptions): 17 | fields = ('name',) 18 | 19 | 20 | translator.register(NeedType, NeedTypeTranslationOptions) 21 | 22 | 23 | class DonationTranslationOptions(TranslationOptions): 24 | fields = ('description', 'donator_name') 25 | 26 | 27 | translator.register(Donation, DonationTranslationOptions) 28 | 29 | 30 | class HospitalTranslationOptions(TranslationOptions): 31 | fields = ('name',) 32 | 33 | 34 | translator.register(Hospital, HospitalTranslationOptions) 35 | 36 | 37 | class RegionTranslationOptions(TranslationOptions): 38 | fields = ('name',) 39 | 40 | 41 | translator.register(Region, RegionTranslationOptions) 42 | 43 | 44 | class DistrictTranslationOptions(TranslationOptions): 45 | fields = ('name',) 46 | 47 | 48 | translator.register(District, DistrictTranslationOptions) 49 | 50 | 51 | class LocalityTranslationOptions(TranslationOptions): 52 | fields = ('name',) 53 | 54 | 55 | translator.register(Locality, LocalityTranslationOptions) 56 | 57 | 58 | class StatisticCategoryTranslationOptions(TranslationOptions): 59 | fields = ('name',) 60 | 61 | 62 | translator.register(StatisticCategory, StatisticCategoryTranslationOptions) 63 | 64 | 65 | class PageTranslationOptions(TranslationOptions): 66 | fields = ('name', 'content') 67 | 68 | 69 | translator.register(Page, PageTranslationOptions) 70 | 71 | 72 | class ContactInfoTranslationOptions(TranslationOptions): 73 | fields = ('text',) 74 | 75 | 76 | translator.register(ContactInfo, ContactInfoTranslationOptions) 77 | -------------------------------------------------------------------------------- /template/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 18 | 19 | 28 | COVID 19 29 | 30 | 31 | 32 |
    33 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /mobile/lib/pages/RootPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:tirek_mobile/pages/LoginPage.dart'; 3 | import 'package:tirek_mobile/services/AuthenticationService.dart'; 4 | import 'package:tirek_mobile/services/SharedPreferencesService.dart'; 5 | 6 | enum AuthStatus { 7 | NOT_DETERMINED, 8 | NOT_LOGGED_IN, 9 | LOGGED_IN, 10 | } 11 | 12 | class RootPage extends StatefulWidget { 13 | RootPage({this.sharedPreferencesService, this.authenticationService}); 14 | 15 | final SharedPreferencesService sharedPreferencesService; 16 | final AuthenticationService authenticationService; 17 | 18 | @override 19 | State createState() => new _RootPageState(); 20 | } 21 | 22 | class _RootPageState extends State { 23 | AuthStatus authStatus = AuthStatus.NOT_DETERMINED; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | // widget.auth.getCurrentUser().then((user) { 29 | // setState(() { 30 | // if (user != null) { 31 | // _userId = user?.uid; 32 | // } 33 | // authStatus = 34 | // user?.uid == null ? AuthStatus.NOT_LOGGED_IN : AuthStatus.LOGGED_IN; 35 | // }); 36 | // }); 37 | } 38 | 39 | void loginCallback() { 40 | // widget.auth.getCurrentUser().then((user) { 41 | // setState(() { 42 | // _userId = user.uid.toString(); 43 | // }); 44 | // }); 45 | // setState(() { 46 | // authStatus = AuthStatus.LOGGED_IN; 47 | // }); 48 | } 49 | 50 | void logoutCallback() { 51 | setState(() { 52 | authStatus = AuthStatus.NOT_LOGGED_IN; 53 | }); 54 | } 55 | 56 | Widget buildWaitingScreen() { 57 | return Scaffold( 58 | body: Container( 59 | alignment: Alignment.center, 60 | child: CircularProgressIndicator(), 61 | ), 62 | ); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return new LoginPage( 68 | authenticationService: widget.authenticationService, 69 | sharedPreferencesService: widget.sharedPreferencesService, 70 | loginCallback: loginCallback, 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /mobile/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /frontend/components/donations/donation.js: -------------------------------------------------------------------------------- 1 | import {Button, Card, Table} from "react-bootstrap"; 2 | import React, {Fragment} from "react"; 3 | import Link from "../navigation/ActiveLink"; 4 | 5 | const Donation = (props) => { 6 | const hideLink = !!props.hideLink; 7 | const item = props.donation; 8 | const details = (item.details || []).map(detail => { 9 | const key = `${item.id} ${detail.id}` 10 | return ( 11 | 12 | 13 | 14 | {detail.need_type.name} 15 | 16 | 17 | 18 | 19 | {detail.amount} {detail.need_type.measure.name} 20 | 21 | 22 | 23 | ) 24 | }); 25 | 26 | const map = { 27 | ORGANIZATION: 'Организация', 28 | PERSONAL: 'Частное лицо', 29 | DONOR: 'Донор', 30 | GOVERNMENT: 'Государство', 31 | } 32 | 33 | 34 | return ( 35 | 36 | 37 | 38 | {item.donator_name} 39 | 40 | 41 | Тип: {map[item.donator_type] || 'Неизвестно'}
    42 | {item.description} 43 |
    44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | {details} 54 | 55 |
    НаименованиеКол-во
    56 | 57 | {!hideLink && 58 | 59 | 60 | 61 | } 62 |
    63 |
    64 | ) 65 | } 66 | 67 | export default Donation; -------------------------------------------------------------------------------- /frontend/components/donations/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {connect} from 'react-redux' 3 | import {bindActionCreators} from 'redux' 4 | import Donation from './donation' 5 | import {Alert, Col, Container, Row, Spinner} from 'react-bootstrap' 6 | import {fetchDonations as fetchDonationsAction,} from '../../actions/creators/donations' 7 | 8 | class Donations extends Component { 9 | componentDidMount() { 10 | this.props.fetchDonationsAction(); 11 | } 12 | 13 | render() { 14 | const {fetching, results} = this.props; 15 | 16 | return ( 17 |
    18 | 19 | 20 | {fetching &&
    } 21 | 22 | 23 |

    Список пожертвований

    24 | 25 | 26 | { 27 | !fetching && results.length > 0 && 28 | results.map(item => 29 |
    30 | 31 |
    32 |
    33 | ) 34 | } 35 | { 36 | !fetching && !results.length && 37 | Нет данных 38 | 39 | } 40 | 41 |
    42 | 43 |
    44 | ); 45 | } 46 | } 47 | 48 | const mapStateToProps = (state) => { 49 | return { 50 | fetching: state.donations.fetching, 51 | results: state.donations.results 52 | } 53 | }; 54 | 55 | const mapDispatchToProps = (dispatch) => bindActionCreators({ 56 | fetchDonationsAction, 57 | }, dispatch); 58 | 59 | export default connect(mapStateToProps, mapDispatchToProps)(Donations) 60 | -------------------------------------------------------------------------------- /backend/distributor/migrations/0004_donation_donationdetail.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.0.4 on 2020-03-25 19:24 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('distributor', '0003_auto_20200325_1806'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Donation', 18 | fields=[ 19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('donator_type', models.CharField(choices=[('ORGANIZATION', 'ORGANIZATION'), ('PERSONAL', 'PERSONAL')], default='ORGANIZATION', max_length=12, verbose_name='Donator Type')), 21 | ('donator_name', models.CharField(max_length=200, verbose_name='Donator Name')), 22 | ('description', models.TextField(max_length=1000, verbose_name='Donation Description')), 23 | ('modified_at', models.DateTimeField(auto_now=True, null=True, verbose_name='Modified Date')), 24 | ('created_at', models.DateTimeField(verbose_name='Created Date')), 25 | ('created_by', models.ForeignKey(auto_created=True, editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='donation_created_by', to=settings.AUTH_USER_MODEL)), 26 | ('modified_by', models.ForeignKey(auto_created=True, editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='donation_modified_by', to=settings.AUTH_USER_MODEL)), 27 | ], 28 | ), 29 | migrations.CreateModel( 30 | name='DonationDetail', 31 | fields=[ 32 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 33 | ('amount', models.PositiveSmallIntegerField(verbose_name='Amount')), 34 | ('donation', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='distributor.Donation', verbose_name='Donation)')), 35 | ('need_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='distributor.NeedType', verbose_name='Need Type')), 36 | ], 37 | ), 38 | ] 39 | --------------------------------------------------------------------------------