├── .gitignore ├── README.md ├── config.json ├── landing ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── static │ └── landing │ │ ├── css │ │ └── style.css │ │ ├── img │ │ ├── face2.png │ │ └── hero1.png │ │ ├── js │ │ └── main.js │ │ └── vendor │ │ ├── aos │ │ ├── aos.css │ │ └── aos.js │ │ ├── bootstrap-icons │ │ ├── bootstrap-icons.css │ │ ├── bootstrap-icons.json │ │ ├── fonts │ │ │ ├── bootstrap-icons.woff │ │ │ └── bootstrap-icons.woff2 │ │ └── index.html │ │ ├── bootstrap │ │ ├── css │ │ │ ├── bootstrap-grid.css │ │ │ ├── bootstrap-grid.css.map │ │ │ ├── bootstrap-grid.min.css │ │ │ ├── bootstrap-grid.min.css.map │ │ │ ├── bootstrap-grid.rtl.css │ │ │ ├── bootstrap-grid.rtl.css.map │ │ │ ├── bootstrap-grid.rtl.min.css │ │ │ ├── bootstrap-grid.rtl.min.css.map │ │ │ ├── bootstrap-reboot.css │ │ │ ├── bootstrap-reboot.css.map │ │ │ ├── bootstrap-reboot.min.css │ │ │ ├── bootstrap-reboot.min.css.map │ │ │ ├── bootstrap-reboot.rtl.css │ │ │ ├── bootstrap-reboot.rtl.css.map │ │ │ ├── bootstrap-reboot.rtl.min.css │ │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ │ ├── bootstrap-utilities.css │ │ │ ├── bootstrap-utilities.css.map │ │ │ ├── bootstrap-utilities.min.css │ │ │ ├── bootstrap-utilities.min.css.map │ │ │ ├── bootstrap-utilities.rtl.css │ │ │ ├── bootstrap-utilities.rtl.css.map │ │ │ ├── bootstrap-utilities.rtl.min.css │ │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ │ ├── bootstrap.css │ │ │ ├── bootstrap.css.map │ │ │ ├── bootstrap.min.css │ │ │ ├── bootstrap.min.css.map │ │ │ ├── bootstrap.rtl.css │ │ │ ├── bootstrap.rtl.css.map │ │ │ ├── bootstrap.rtl.min.css │ │ │ └── bootstrap.rtl.min.css.map │ │ └── js │ │ │ ├── bootstrap.bundle.js │ │ │ ├── bootstrap.bundle.js.map │ │ │ ├── bootstrap.bundle.min.js │ │ │ ├── bootstrap.bundle.min.js.map │ │ │ ├── bootstrap.esm.js │ │ │ ├── bootstrap.esm.js.map │ │ │ ├── bootstrap.esm.min.js │ │ │ ├── bootstrap.esm.min.js.map │ │ │ ├── bootstrap.js │ │ │ ├── bootstrap.js.map │ │ │ ├── bootstrap.min.js │ │ │ └── bootstrap.min.js.map │ │ ├── boxicons │ │ ├── css │ │ │ ├── animations.css │ │ │ ├── boxicons.css │ │ │ ├── boxicons.min.css │ │ │ └── transformations.css │ │ └── fonts │ │ │ ├── boxicons.eot │ │ │ ├── boxicons.svg │ │ │ ├── boxicons.ttf │ │ │ ├── boxicons.woff │ │ │ └── boxicons.woff2 │ │ ├── glightbox │ │ ├── css │ │ │ ├── glightbox.css │ │ │ └── glightbox.min.css │ │ └── js │ │ │ ├── glightbox.js │ │ │ └── glightbox.min.js │ │ ├── isotope-layout │ │ ├── isotope.pkgd.js │ │ └── isotope.pkgd.min.js │ │ ├── remixicon │ │ ├── remixicon.css │ │ ├── remixicon.eot │ │ ├── remixicon.less │ │ ├── remixicon.svg │ │ ├── remixicon.symbol.svg │ │ ├── remixicon.ttf │ │ ├── remixicon.woff │ │ └── remixicon.woff2 │ │ ├── swiper │ │ ├── swiper-bundle.min.css │ │ └── swiper-bundle.min.js │ │ └── waypoints │ │ └── noframework.waypoints.js ├── templates │ └── landing │ │ ├── base.html │ │ └── index.html ├── tests.py ├── urls.py └── views.py ├── manage.py ├── msengage ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py ├── people ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_rename_misingperson_missingperson_and_more.py │ ├── 0003_alter_missingperson_contact_person_and_more.py │ ├── 0004_alter_missingperson_phone.py │ ├── 0004_missingperson_face_id_missingperson_is_verified.py │ ├── 0005_reportedperson.py │ ├── 0006_reportedperson_is_matched_with_missing_person_and_more.py │ ├── 0007_reportedperson_matched_face_id.py │ ├── 0008_merge_20220520_1911.py │ ├── 0009_alter_missingperson_age_and_more.py │ ├── 0010_alter_reportedperson_description_and_more.py │ ├── 0011_missingperson_contact_email.py │ ├── 0012_alter_missingperson_contact_email.py │ ├── 0013_missingperson_found_location.py │ ├── 0014_rename_last_seen_reportedperson_reported_location.py │ ├── 0015_missingperson_found_time.py │ ├── 0016_missingperson_is_contacted.py │ └── __init__.py ├── models.py ├── templates │ └── people │ │ ├── create_update_form.html │ │ ├── delete_form.html │ │ ├── found_person_details.html │ │ ├── missing_person_form_success.html │ │ ├── missing_person_list.html │ │ ├── missing_person_matched.html │ │ ├── reported_create_update_form.html │ │ ├── reported_delete_form.html │ │ ├── reported_person_form_success.html │ │ └── reported_person_list.html ├── tests.py ├── urls.py └── views.py ├── requirements.txt ├── screenshots ├── details_of_match.png ├── diagrams │ ├── business_architecture.png │ ├── lessons_learnt.png │ └── tech_architecture.png └── email_received.png └── templates └── registration ├── logged_out.html ├── login.html ├── password_reset_complete.html ├── password_reset_confirm.html ├── password_reset_done.html ├── password_reset_email.html └── password_reset_form.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Django # 2 | *.log 3 | *.pot 4 | *.pyc 5 | __pycache__ 6 | msengage.sqlite3 7 | static 8 | media 9 | # Environments 10 | .env 11 | .venv 12 | env/ 13 | venv/ 14 | ENV/ 15 | env.bak/ 16 | venv.bak/ 17 | # Visual Studio Code # 18 | .vscode/* 19 | !.vscode/settings.json 20 | !.vscode/tasks.json 21 | !.vscode/launch.json 22 | !.vscode/extensions.json 23 | .history -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 |
8 | 9 | Logo 10 | 11 | 12 | 13 |

FACE FIND

14 | 15 |

Submission for Microsoft Engage 2022

16 | 17 |

18 | 19 | A Django Web Application to help find missing people using Face Recognition. 20 | 21 | Now deployed on Heroku! Click [here](https://facefindxyz.herokuapp.com/) to check it out! 22 |
23 | 24 | [![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/tinycoder2/FaceFind?logo=github&style=for-the-badge)](https://github.com/tinycoder2/FaceFind) 25 | [![GitHub last commit](https://img.shields.io/github/last-commit/tinycoder2/FaceFind?style=for-the-badge&logo=git)](https://github.com/tinycoder2/FaceFind) 26 | [![Languages](https://img.shields.io/github/languages/count/tinycoder2/FaceFind?style=for-the-badge)](https://github.com/tinycoder2/FaceFind) 27 | [![Generic badge](https://img.shields.io/badge/view-demo-blue?style=for-the-badge&label=View%20Demo%20Video)](https://youtu.be/onXOQIu5jCg) 28 | 29 |
30 | 31 |

32 | 33 | 34 | 35 | 36 |
37 | Table of Contents 38 |
    39 |
  1. About The Project 40 | 43 |
  2. 44 |
  3. 45 | Getting Started 46 | 51 |
  4. 52 |
  5. Business Logic
  6. 53 |
  7. Tech Architecture
  8. 54 |
  9. Features and Interfaces
  10. 55 |
  11. Discussions
  12. 56 |
  13. Lessons Learnt
  14. 57 |
  15. Future Work
  16. 58 |
59 |
60 | 61 | 62 | 63 | ## About The Project 64 | The thought of a family member, a friend or someone else you care about going missing can be terrifying. This project aims to help find your loved ones using Face Recognition Technology. If someone you know is missing, then, 65 | - Register the missing person with us. 66 | - Once the background check is done and the missing person is verified, we generate a unique Face ID for the missing person using Azure's Face API. 67 | - When volunteers report a suspected missing person, we verify and generate a Face ID the same way. We then use Azure's Find Similar API to identify a potential match with our database of missing person Face IDs. 68 | - If a match is found we will contact you. 69 |

(back to top)

70 | 71 | ### Built With 72 | To achieve the goal of finding missing people, I made use of the following tools and languages, 73 |

74 | bootstrap 75 | css3 76 | javascript 77 | html5 78 | express 79 | express 80 | express 81 | express 82 | git 83 | heroku 84 |

(back to top)

85 | 86 | 87 | ## Getting Started 88 | ### Prerequisites 89 | Firstly you have to make sure you have python installed. If you don't have python, you can get it [here](https://www.python.org/downloads/). 90 | * Use this command to check if python is installed, 91 | ```sh 92 | python --version 93 | ``` 94 | ### Getting API credentials 95 | To access Azure Cognitive Services REST API for Face Analysis, you will need, 96 | - An Azure account (you can create your account for free [here](https://azure.microsoft.com/en-au/free/)) 97 | - A Computer Vision Resource in your Azure account 98 | 99 | To create a Computer Vision Resource, you can navigate through the portal, create a new resource and go under the class of “AI+Machine Learning”. Then select the Face Cognitive Service and set the required information. 100 | 101 | Under the Resource Management tab you will find “Keys and Endpoint”. From this section, copy one of the two Keys and the Endpoint and paste it somewhere safe. We will be using these in the `config.json` file. 102 | 103 | For detailed instructions click [here.](https://medium.com/microsoftazure/azure-cognitive-services-rest-api-for-computer-vision-cf782e975837) 104 | 105 | ### Setup 106 | 107 | _To get a local copy of the project and run it, follow these steps._ 108 | 109 | 1. Create a folder in which you want set up the project. Go into that folder and check if python is installed. 110 | 111 | ```sh 112 | mkdir myFolder 113 | cd myFolder 114 | python --version 115 | 116 | ``` 117 | 118 | 2. Clone the repository: 119 | 120 | ```sh 121 | git clone https://github.com/tinycoder2/FaceFind.git 122 | ``` 123 | 124 | 3. Create a virtual environment to install dependencies in and activate it: 125 | 126 | ```sh 127 | python -m venv myEnv 128 | cd myEnv 129 | .\Scripts\activate 130 | cd .. 131 | ``` 132 | 133 | 4. Then install the dependencies: 134 | 135 | ```sh 136 | cd FaceFind 137 | pip install -r requirements.txt 138 | ``` 139 | 140 | 141 | 142 | 5. Enter your API KEY, ENDPOINT that we got from Getting API credentials, EMAIL ID and EMAIL PASSWORD in `config.json` 143 | 144 | ```json 145 | { 146 | "KEY": "Your Azure API KEY", 147 | "ENDPOINT": "Your Azure Endpoint", 148 | "EMAIL-ID": "Your Email ID from which the app will contact is missing person is found", 149 | "EMAIL-PASSWORD": "Password for said Email ID" 150 | } 151 | ``` 152 | 153 | To allow the app access your email account, go to [google account setting](https://myaccount.google.com/intro/security?hl=en) , security tab and ensure that you have *Less secure app access* turned *on.* 154 | 155 | 6. Go to `msengage\settings.py` and change line number 18 as, 156 | 157 | ```py 158 | # Change to False if cloning and running on local host 159 | IS_DEPLOYED_ON_HEROKU = False 160 | ``` 161 | 162 | 7. Apply migrations: 163 | 164 | ```sh 165 | python manage.py migrate 166 | ``` 167 | 168 | 8. Create admin account : 169 | 170 | ```sh 171 | python manage.py createsuperuser 172 | ``` 173 | 174 | Follow the promt and enter the username, preferably *"admin"*, desired email and password. Make note of the username and password as you will have to use these credentials to login. 175 | 176 | 9. Run server: 177 | 178 | ```sh 179 | python manage.py runserver 180 | ``` 181 | 182 | The app is now running at `http://127.0.0.1:8000/` 183 | 184 |

(back to top)

185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | ## Business Logic 193 | 194 | businessArchitecture 195 | 196 |

(back to top)

197 | 198 | 199 | 200 | 201 | 202 | ## Tech Architecture 203 | 204 | techArchitecture 205 | 206 |

(back to top)

207 | 208 | 209 | 210 | 211 | 212 | ## Features and Interfaces 213 | ### Home Screen 214 | - Landing page displaying hero section, about section, technology used and FAQ section. 215 | 216 | https://user-images.githubusercontent.com/72341529/170797626-e4a6fc58-5665-435a-99a0-cff8b7e84a64.mp4 217 | 218 | ### Registering a Missing Person 219 | 220 | https://user-images.githubusercontent.com/72341529/170797691-54077207-c774-48f9-bcab-6db4be10e198.mp4 221 | 222 | 223 | ### Reporting a Suspected Missing Person 224 | 225 | https://user-images.githubusercontent.com/72341529/170797730-6b5c78b9-ab0f-4bf3-8237-7c6a5d64820c.mp4 226 | 227 | ### Admin Login and Views 228 | - Logging in as the admin and displaying site from admin POV. 229 | 230 | https://user-images.githubusercontent.com/72341529/170797751-4897c355-0f34-4d25-88ca-16e45019ddee.mp4 231 | 232 | ### Missing People Views 233 | - Viewing all missing people, ones that need to be approved, approving a missing person (background check), viewing missing people with status as Leads, status as Found, Deleting a missing person, Editing the details of a missing person. 234 | 235 | https://user-images.githubusercontent.com/72341529/170797773-ac2bd097-2e3b-44dc-94b0-7c47003ecbcb.mp4 236 | 237 | ### Reported Person Views 238 | - Viewing all reported people, ones that need to be approved, approving a reported person (checking validity of report), viewing details of potential match, confirming the match, status of missing person changing from Leads to Found. 239 | 240 | 241 | https://user-images.githubusercontent.com/72341529/170797791-7d0093a4-8dec-4f70-ba61-35aafb594007.mp4 242 | 243 | 244 | ### Details of Match Found 245 | detailsOfMatchFound 246 | 247 | ### Email Received 248 | emailReceived 249 | 250 | 251 | 252 |

(back to top)

253 | 254 | 255 | 256 | 257 | 258 | ## Discussions 259 | 260 | - Face Detection 261 | 262 | - To detect the face in the image the person uploads, we use the [Detect With Stream API](https://docs.microsoft.com/en-us/rest/api/faceapi/face/detect-with-stream). 263 | 264 | - In `people\views.py` we have a function `generate_face_id` that uses the Detect With Stream API to get the faceID, which is an identifier of the face feature and will be used in [Face - Find Similar](https://docs.microsoft.com/en-us/rest/api/faceapi/face/findsimilar). 265 | 266 | ```py 267 | 268 | # function to generate face_id using Azure Face API 269 | 270 | def generate_face_id(image_path): 271 | face_client = FaceClient(config['ENDPOINT'], CognitiveServicesCredentials(config['KEY'])) 272 | response_detected_face = face_client.face.detect_with_stream( 273 | image=open(image_path, 'rb'), 274 | detection_model='detection_03', 275 | recognition_model='recognition_04', 276 | ) 277 | return response_detected_face 278 | 279 | ``` 280 | 281 | - Face Recognition 282 | 283 | - Given query face's faceID, to search the similar-looking faces from a `faceID array`, which is an array of faceIDs generated from [Detect With Stream API](https://docs.microsoft.com/en-us/rest/api/faceapi/face/detect-with-stream), we use the [Face - Find Similar API](https://docs.microsoft.com/en-us/rest/api/faceapi/face/findsimilar). 284 | 285 | - In `people\views.py` we have a function `find_match` that uses this API to find a match for the reported person from the list of missing people faceIDs. 286 | 287 | ```py 288 | # function to find a match for the reported person from the list of missing people using Azure Face API 289 | def find_match(reported_face_id, missing_face_ids): 290 | face_client = FaceClient(config['ENDPOINT'], CognitiveServicesCredentials(config['KEY'])) 291 | matched_faces = face_client.face.find_similar( 292 | face_id=reported_face_id, 293 | face_ids=missing_face_id 294 | ) 295 | return matched_faces 296 | ``` 297 | 298 | - Given the timeframe for this project and the fact that I was using a free account with limited number of API calls, I have built my **MVP** with these two APIs. 299 | 300 | - In the future I plan to make use of a `faceListId` instead of the `faceID array` for the [Face - Find Similar API](https://docs.microsoft.com/en-us/rest/api/faceapi/face/findsimilar). 301 | 302 | - The major difference between these two is that `faceID array` contains the faces created by [Face - Detect With Url](https://docs.microsoft.com/en-us/rest/api/faceapi/face/detectwithurl) or [Face - Detect With Stream](https://docs.microsoft.com/en-us/rest/api/faceapi/face/detectwithstream), which will expire at the time specified by faceIdTimeToLive after creation, which is about 86400 seconds (24 hours) by default. A `faceListId` is created by [FaceList - Create](https://docs.microsoft.com/en-us/rest/api/faceapi/facelist/create) containing persistedFaceIds that will not expire. 303 | 304 | - Furthermore, one could also use [PersonGroup](https://docs.microsoft.com/en-us/rest/api/faceapi/persongroup) / [LargePersonGroup](https://docs.microsoft.com/en-us/rest/api/faceapi/largepersongroup) and [Face - Identify](https://docs.microsoft.com/en-us/rest/api/faceapi/face/identify) when the face number is large, the [LargeFaceList](https://docs.microsoft.com/en-us/rest/api/faceapi/largefacelist) can support up to 1,000,000 faces. 305 | 306 | 307 |

(back to top)

308 | 309 | 310 | ## Lessons Learnt 311 | 312 | lessonsLearnt 313 | 314 |

(back to top)

315 | 316 | 317 | 318 | ## Future Work 319 | 320 | - Future plans include, 321 | 322 | - SMS verification for registering a missing person 323 | 324 | - Geo location from IP address while reporting suspected missing person. 325 | 326 | - Using Azure's [PersonGroup](https://docs.microsoft.com/en-us/rest/api/faceapi/persongroup) / [LargePersonGroup](https://docs.microsoft.com/en-us/rest/api/faceapi/largepersongroup) and [Face - Identify](https://docs.microsoft.com/en-us/rest/api/faceapi/face/identify) for face detection and identification. 327 | 328 | - Sanitizing traffic to prevent API throttling. 329 | 330 |

(back to top)

331 | 332 | 333 | 334 | 335 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "KEY": "Your Azure API KEY", 3 | "ENDPOINT": "Your Azure Endpoint", 4 | "EMAIL-ID": "Your Email ID from which the app will contact if missing person is found", 5 | "EMAIL-PASSWORD": "Password for said Email ID" 6 | } -------------------------------------------------------------------------------- /landing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/__init__.py -------------------------------------------------------------------------------- /landing/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /landing/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LandingConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'landing' 7 | -------------------------------------------------------------------------------- /landing/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/migrations/__init__.py -------------------------------------------------------------------------------- /landing/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /landing/static/landing/img/face2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/img/face2.png -------------------------------------------------------------------------------- /landing/static/landing/img/hero1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/img/hero1.png -------------------------------------------------------------------------------- /landing/static/landing/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Template Name: Arsha - v4.7.1 3 | * Template URL: https://bootstrapmade.com/arsha-free-bootstrap-html-template-corporate/ 4 | * Author: BootstrapMade.com 5 | * License: https://bootstrapmade.com/license/ 6 | */ 7 | (function () { 8 | "use strict"; 9 | 10 | /** 11 | * Easy selector helper function 12 | */ 13 | const select = (el, all = false) => { 14 | el = el.trim() 15 | if (all) { 16 | return [...document.querySelectorAll(el)] 17 | } else { 18 | return document.querySelector(el) 19 | } 20 | } 21 | 22 | /** 23 | * Easy event listener function 24 | */ 25 | const on = (type, el, listener, all = false) => { 26 | let selectEl = select(el, all) 27 | if (selectEl) { 28 | if (all) { 29 | selectEl.forEach(e => e.addEventListener(type, listener)) 30 | } else { 31 | selectEl.addEventListener(type, listener) 32 | } 33 | } 34 | } 35 | 36 | /** 37 | * Easy on scroll event listener 38 | */ 39 | const onscroll = (el, listener) => { 40 | el.addEventListener('scroll', listener) 41 | } 42 | 43 | /** 44 | * Navbar links active state on scroll 45 | */ 46 | let navbarlinks = select('#navbar .scrollto', true) 47 | const navbarlinksActive = () => { 48 | let position = window.scrollY + 200 49 | navbarlinks.forEach(navbarlink => { 50 | if (!navbarlink.hash) return 51 | let section = select(navbarlink.hash) 52 | if (!section) return 53 | if (position >= section.offsetTop && position <= (section.offsetTop + section.offsetHeight)) { 54 | navbarlink.classList.add('active') 55 | } else { 56 | navbarlink.classList.remove('active') 57 | } 58 | }) 59 | } 60 | window.addEventListener('load', navbarlinksActive) 61 | onscroll(document, navbarlinksActive) 62 | 63 | /** 64 | * Scrolls to an element with header offset 65 | */ 66 | const scrollto = (el) => { 67 | let header = select('#header') 68 | let offset = header.offsetHeight 69 | 70 | let elementPos = select(el).offsetTop 71 | window.scrollTo({ 72 | top: elementPos - offset, 73 | behavior: 'smooth' 74 | }) 75 | } 76 | 77 | /** 78 | * Toggle .header-scrolled class to #header when page is scrolled 79 | */ 80 | let selectHeader = select('#header') 81 | if (selectHeader) { 82 | const headerScrolled = () => { 83 | if (window.scrollY > 100) { 84 | selectHeader.classList.add('header-scrolled') 85 | } else { 86 | selectHeader.classList.remove('header-scrolled') 87 | } 88 | } 89 | window.addEventListener('load', headerScrolled) 90 | onscroll(document, headerScrolled) 91 | } 92 | 93 | /** 94 | * Back to top button 95 | */ 96 | let backtotop = select('.back-to-top') 97 | if (backtotop) { 98 | const toggleBacktotop = () => { 99 | if (window.scrollY > 100) { 100 | backtotop.classList.add('active') 101 | } else { 102 | backtotop.classList.remove('active') 103 | } 104 | } 105 | window.addEventListener('load', toggleBacktotop) 106 | onscroll(document, toggleBacktotop) 107 | } 108 | 109 | /** 110 | * Mobile nav toggle 111 | */ 112 | on('click', '.mobile-nav-toggle', function (e) { 113 | select('#navbar').classList.toggle('navbar-mobile') 114 | this.classList.toggle('bi-list') 115 | this.classList.toggle('bi-x') 116 | }) 117 | 118 | /** 119 | * Mobile nav dropdowns activate 120 | */ 121 | on('click', '.navbar .dropdown > a', function (e) { 122 | if (select('#navbar').classList.contains('navbar-mobile')) { 123 | e.preventDefault() 124 | this.nextElementSibling.classList.toggle('dropdown-active') 125 | } 126 | }, true) 127 | 128 | /** 129 | * Scrool with ofset on links with a class name .scrollto 130 | */ 131 | on('click', '.scrollto', function (e) { 132 | if (select(this.hash)) { 133 | e.preventDefault() 134 | 135 | let navbar = select('#navbar') 136 | if (navbar.classList.contains('navbar-mobile')) { 137 | navbar.classList.remove('navbar-mobile') 138 | let navbarToggle = select('.mobile-nav-toggle') 139 | navbarToggle.classList.toggle('bi-list') 140 | navbarToggle.classList.toggle('bi-x') 141 | } 142 | scrollto(this.hash) 143 | } 144 | }, true) 145 | 146 | /** 147 | * Scroll with ofset on page load with hash links in the url 148 | */ 149 | window.addEventListener('load', () => { 150 | if (window.location.hash) { 151 | if (select(window.location.hash)) { 152 | scrollto(window.location.hash) 153 | } 154 | } 155 | }); 156 | 157 | /** 158 | * Preloader 159 | */ 160 | let preloader = select('#preloader'); 161 | if (preloader) { 162 | window.addEventListener('load', () => { 163 | preloader.remove() 164 | }); 165 | } 166 | 167 | /** 168 | * Initiate glightbox 169 | */ 170 | const glightbox = GLightbox({ 171 | selector: '.glightbox' 172 | }); 173 | 174 | 175 | 176 | 177 | 178 | /** 179 | * Animation on scroll 180 | */ 181 | window.addEventListener('load', () => { 182 | AOS.init({ 183 | duration: 1000, 184 | easing: "ease-in-out", 185 | once: true, 186 | mirror: false 187 | }); 188 | }); 189 | 190 | })() -------------------------------------------------------------------------------- /landing/static/landing/vendor/aos/aos.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AOS=t():e.AOS=t()}(this,function(){return function(e){function t(o){if(n[o])return n[o].exports;var i=n[o]={exports:{},id:o,loaded:!1};return e[o].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="dist/",t(0)}([function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}var i=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]&&arguments[0];if(e&&(k=!0),k)return w=(0,y.default)(w,x),(0,b.default)(w,x.once),w},O=function(){w=(0,h.default)(),j()},M=function(){w.forEach(function(e,t){e.node.removeAttribute("data-aos"),e.node.removeAttribute("data-aos-easing"),e.node.removeAttribute("data-aos-duration"),e.node.removeAttribute("data-aos-delay")})},S=function(e){return e===!0||"mobile"===e&&p.default.mobile()||"phone"===e&&p.default.phone()||"tablet"===e&&p.default.tablet()||"function"==typeof e&&e()===!0},_=function(e){x=i(x,e),w=(0,h.default)();var t=document.all&&!window.atob;return S(x.disable)||t?M():(x.disableMutationObserver||d.default.isSupported()||(console.info('\n aos: MutationObserver is not supported on this browser,\n code mutations observing has been disabled.\n You may have to call "refreshHard()" by yourself.\n '),x.disableMutationObserver=!0),document.querySelector("body").setAttribute("data-aos-easing",x.easing),document.querySelector("body").setAttribute("data-aos-duration",x.duration),document.querySelector("body").setAttribute("data-aos-delay",x.delay),"DOMContentLoaded"===x.startEvent&&["complete","interactive"].indexOf(document.readyState)>-1?j(!0):"load"===x.startEvent?window.addEventListener(x.startEvent,function(){j(!0)}):document.addEventListener(x.startEvent,function(){j(!0)}),window.addEventListener("resize",(0,s.default)(j,x.debounceDelay,!0)),window.addEventListener("orientationchange",(0,s.default)(j,x.debounceDelay,!0)),window.addEventListener("scroll",(0,u.default)(function(){(0,b.default)(w,x.once)},x.throttleDelay)),x.disableMutationObserver||d.default.ready("[data-aos]",O),w)};e.exports={init:_,refresh:j,refreshHard:O}},function(e,t){},,,,,function(e,t){(function(t){"use strict";function n(e,t,n){function o(t){var n=b,o=v;return b=v=void 0,k=t,g=e.apply(o,n)}function r(e){return k=e,h=setTimeout(f,t),M?o(e):g}function a(e){var n=e-w,o=e-k,i=t-n;return S?j(i,y-o):i}function c(e){var n=e-w,o=e-k;return void 0===w||n>=t||n<0||S&&o>=y}function f(){var e=O();return c(e)?d(e):void(h=setTimeout(f,a(e)))}function d(e){return h=void 0,_&&b?o(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),k=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(O())}function m(){var e=O(),n=c(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(f,t),o(w)}return void 0===h&&(h=setTimeout(f,t)),g}var b,v,y,g,h,w,k=0,M=!1,S=!1,_=!0;if("function"!=typeof e)throw new TypeError(s);return t=u(t)||0,i(n)&&(M=!!n.leading,S="maxWait"in n,y=S?x(u(n.maxWait)||0,t):y,_="trailing"in n?!!n.trailing:_),m.cancel=l,m.flush=p,m}function o(e,t,o){var r=!0,a=!0;if("function"!=typeof e)throw new TypeError(s);return i(o)&&(r="leading"in o?!!o.leading:r,a="trailing"in o?!!o.trailing:a),n(e,t,{leading:r,maxWait:t,trailing:a})}function i(e){var t="undefined"==typeof e?"undefined":c(e);return!!e&&("object"==t||"function"==t)}function r(e){return!!e&&"object"==("undefined"==typeof e?"undefined":c(e))}function a(e){return"symbol"==("undefined"==typeof e?"undefined":c(e))||r(e)&&k.call(e)==d}function u(e){if("number"==typeof e)return e;if(a(e))return f;if(i(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=i(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(l,"");var n=m.test(e);return n||b.test(e)?v(e.slice(2),n?2:8):p.test(e)?f:+e}var c="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},s="Expected a function",f=NaN,d="[object Symbol]",l=/^\s+|\s+$/g,p=/^[-+]0x[0-9a-f]+$/i,m=/^0b[01]+$/i,b=/^0o[0-7]+$/i,v=parseInt,y="object"==("undefined"==typeof t?"undefined":c(t))&&t&&t.Object===Object&&t,g="object"==("undefined"==typeof self?"undefined":c(self))&&self&&self.Object===Object&&self,h=y||g||Function("return this")(),w=Object.prototype,k=w.toString,x=Math.max,j=Math.min,O=function(){return h.Date.now()};e.exports=o}).call(t,function(){return this}())},function(e,t){(function(t){"use strict";function n(e,t,n){function i(t){var n=b,o=v;return b=v=void 0,O=t,g=e.apply(o,n)}function r(e){return O=e,h=setTimeout(f,t),M?i(e):g}function u(e){var n=e-w,o=e-O,i=t-n;return S?x(i,y-o):i}function s(e){var n=e-w,o=e-O;return void 0===w||n>=t||n<0||S&&o>=y}function f(){var e=j();return s(e)?d(e):void(h=setTimeout(f,u(e)))}function d(e){return h=void 0,_&&b?i(e):(b=v=void 0,g)}function l(){void 0!==h&&clearTimeout(h),O=0,b=w=v=h=void 0}function p(){return void 0===h?g:d(j())}function m(){var e=j(),n=s(e);if(b=arguments,v=this,w=e,n){if(void 0===h)return r(w);if(S)return h=setTimeout(f,t),i(w)}return void 0===h&&(h=setTimeout(f,t)),g}var b,v,y,g,h,w,O=0,M=!1,S=!1,_=!0;if("function"!=typeof e)throw new TypeError(c);return t=a(t)||0,o(n)&&(M=!!n.leading,S="maxWait"in n,y=S?k(a(n.maxWait)||0,t):y,_="trailing"in n?!!n.trailing:_),m.cancel=l,m.flush=p,m}function o(e){var t="undefined"==typeof e?"undefined":u(e);return!!e&&("object"==t||"function"==t)}function i(e){return!!e&&"object"==("undefined"==typeof e?"undefined":u(e))}function r(e){return"symbol"==("undefined"==typeof e?"undefined":u(e))||i(e)&&w.call(e)==f}function a(e){if("number"==typeof e)return e;if(r(e))return s;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(d,"");var n=p.test(e);return n||m.test(e)?b(e.slice(2),n?2:8):l.test(e)?s:+e}var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},c="Expected a function",s=NaN,f="[object Symbol]",d=/^\s+|\s+$/g,l=/^[-+]0x[0-9a-f]+$/i,p=/^0b[01]+$/i,m=/^0o[0-7]+$/i,b=parseInt,v="object"==("undefined"==typeof t?"undefined":u(t))&&t&&t.Object===Object&&t,y="object"==("undefined"==typeof self?"undefined":u(self))&&self&&self.Object===Object&&self,g=v||y||Function("return this")(),h=Object.prototype,w=h.toString,k=Math.max,x=Math.min,j=function(){return g.Date.now()};e.exports=n}).call(t,function(){return this}())},function(e,t){"use strict";function n(e){var t=void 0,o=void 0,i=void 0;for(t=0;te.position?e.node.classList.add("aos-animate"):"undefined"!=typeof o&&("false"===o||!n&&"true"!==o)&&e.node.classList.remove("aos-animate")},o=function(e,t){var o=window.pageYOffset,i=window.innerHeight;e.forEach(function(e,r){n(e,i+o,t)})};t.default=o},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(12),r=o(i),a=function(e,t){return e.forEach(function(e,n){e.node.classList.add("aos-init"),e.position=(0,r.default)(e.node,t.offset)}),e};t.default=a},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(13),r=o(i),a=function(e,t){var n=0,o=0,i=window.innerHeight,a={offset:e.getAttribute("data-aos-offset"),anchor:e.getAttribute("data-aos-anchor"),anchorPlacement:e.getAttribute("data-aos-anchor-placement")};switch(a.offset&&!isNaN(a.offset)&&(o=parseInt(a.offset)),a.anchor&&document.querySelectorAll(a.anchor)&&(e=document.querySelectorAll(a.anchor)[0]),n=(0,r.default)(e).top,a.anchorPlacement){case"top-bottom":break;case"center-bottom":n+=e.offsetHeight/2;break;case"bottom-bottom":n+=e.offsetHeight;break;case"top-center":n+=i/2;break;case"bottom-center":n+=i/2+e.offsetHeight;break;case"center-center":n+=i/2+e.offsetHeight/2;break;case"top-top":n+=i;break;case"bottom-top":n+=e.offsetHeight+i;break;case"center-top":n+=e.offsetHeight/2+i}return a.anchorPlacement||a.offset||isNaN(t)||(o=t),n+o};t.default=a},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){for(var t=0,n=0;e&&!isNaN(e.offsetLeft)&&!isNaN(e.offsetTop);)t+=e.offsetLeft-("BODY"!=e.tagName?e.scrollLeft:0),n+=e.offsetTop-("BODY"!=e.tagName?e.scrollTop:0),e=e.offsetParent;return{top:n,left:t}};t.default=n},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){return e=e||document.querySelectorAll("[data-aos]"),Array.prototype.map.call(e,function(e){return{node:e}})};t.default=n}])}); -------------------------------------------------------------------------------- /landing/static/landing/vendor/bootstrap-icons/fonts/bootstrap-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/bootstrap-icons/fonts/bootstrap-icons.woff -------------------------------------------------------------------------------- /landing/static/landing/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 -------------------------------------------------------------------------------- /landing/static/landing/vendor/bootstrap/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | :root { 9 | --bs-blue: #0d6efd; 10 | --bs-indigo: #6610f2; 11 | --bs-purple: #6f42c1; 12 | --bs-pink: #d63384; 13 | --bs-red: #dc3545; 14 | --bs-orange: #fd7e14; 15 | --bs-yellow: #ffc107; 16 | --bs-green: #198754; 17 | --bs-teal: #20c997; 18 | --bs-cyan: #0dcaf0; 19 | --bs-white: #fff; 20 | --bs-gray: #6c757d; 21 | --bs-gray-dark: #343a40; 22 | --bs-gray-100: #f8f9fa; 23 | --bs-gray-200: #e9ecef; 24 | --bs-gray-300: #dee2e6; 25 | --bs-gray-400: #ced4da; 26 | --bs-gray-500: #adb5bd; 27 | --bs-gray-600: #6c757d; 28 | --bs-gray-700: #495057; 29 | --bs-gray-800: #343a40; 30 | --bs-gray-900: #212529; 31 | --bs-primary: #0d6efd; 32 | --bs-secondary: #6c757d; 33 | --bs-success: #198754; 34 | --bs-info: #0dcaf0; 35 | --bs-warning: #ffc107; 36 | --bs-danger: #dc3545; 37 | --bs-light: #f8f9fa; 38 | --bs-dark: #212529; 39 | --bs-primary-rgb: 13, 110, 253; 40 | --bs-secondary-rgb: 108, 117, 125; 41 | --bs-success-rgb: 25, 135, 84; 42 | --bs-info-rgb: 13, 202, 240; 43 | --bs-warning-rgb: 255, 193, 7; 44 | --bs-danger-rgb: 220, 53, 69; 45 | --bs-light-rgb: 248, 249, 250; 46 | --bs-dark-rgb: 33, 37, 41; 47 | --bs-white-rgb: 255, 255, 255; 48 | --bs-black-rgb: 0, 0, 0; 49 | --bs-body-color-rgb: 33, 37, 41; 50 | --bs-body-bg-rgb: 255, 255, 255; 51 | --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 52 | --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 53 | --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); 54 | --bs-body-font-family: var(--bs-font-sans-serif); 55 | --bs-body-font-size: 1rem; 56 | --bs-body-font-weight: 400; 57 | --bs-body-line-height: 1.5; 58 | --bs-body-color: #212529; 59 | --bs-body-bg: #fff; 60 | } 61 | 62 | *, 63 | *::before, 64 | *::after { 65 | box-sizing: border-box; 66 | } 67 | 68 | @media (prefers-reduced-motion: no-preference) { 69 | :root { 70 | scroll-behavior: smooth; 71 | } 72 | } 73 | 74 | body { 75 | margin: 0; 76 | font-family: var(--bs-body-font-family); 77 | font-size: var(--bs-body-font-size); 78 | font-weight: var(--bs-body-font-weight); 79 | line-height: var(--bs-body-line-height); 80 | color: var(--bs-body-color); 81 | text-align: var(--bs-body-text-align); 82 | background-color: var(--bs-body-bg); 83 | -webkit-text-size-adjust: 100%; 84 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 85 | } 86 | 87 | hr { 88 | margin: 1rem 0; 89 | color: inherit; 90 | background-color: currentColor; 91 | border: 0; 92 | opacity: 0.25; 93 | } 94 | 95 | hr:not([size]) { 96 | height: 1px; 97 | } 98 | 99 | h6, h5, h4, h3, h2, h1 { 100 | margin-top: 0; 101 | margin-bottom: 0.5rem; 102 | font-weight: 500; 103 | line-height: 1.2; 104 | } 105 | 106 | h1 { 107 | font-size: calc(1.375rem + 1.5vw); 108 | } 109 | @media (min-width: 1200px) { 110 | h1 { 111 | font-size: 2.5rem; 112 | } 113 | } 114 | 115 | h2 { 116 | font-size: calc(1.325rem + 0.9vw); 117 | } 118 | @media (min-width: 1200px) { 119 | h2 { 120 | font-size: 2rem; 121 | } 122 | } 123 | 124 | h3 { 125 | font-size: calc(1.3rem + 0.6vw); 126 | } 127 | @media (min-width: 1200px) { 128 | h3 { 129 | font-size: 1.75rem; 130 | } 131 | } 132 | 133 | h4 { 134 | font-size: calc(1.275rem + 0.3vw); 135 | } 136 | @media (min-width: 1200px) { 137 | h4 { 138 | font-size: 1.5rem; 139 | } 140 | } 141 | 142 | h5 { 143 | font-size: 1.25rem; 144 | } 145 | 146 | h6 { 147 | font-size: 1rem; 148 | } 149 | 150 | p { 151 | margin-top: 0; 152 | margin-bottom: 1rem; 153 | } 154 | 155 | abbr[title], 156 | abbr[data-bs-original-title] { 157 | -webkit-text-decoration: underline dotted; 158 | text-decoration: underline dotted; 159 | cursor: help; 160 | -webkit-text-decoration-skip-ink: none; 161 | text-decoration-skip-ink: none; 162 | } 163 | 164 | address { 165 | margin-bottom: 1rem; 166 | font-style: normal; 167 | line-height: inherit; 168 | } 169 | 170 | ol, 171 | ul { 172 | padding-left: 2rem; 173 | } 174 | 175 | ol, 176 | ul, 177 | dl { 178 | margin-top: 0; 179 | margin-bottom: 1rem; 180 | } 181 | 182 | ol ol, 183 | ul ul, 184 | ol ul, 185 | ul ol { 186 | margin-bottom: 0; 187 | } 188 | 189 | dt { 190 | font-weight: 700; 191 | } 192 | 193 | dd { 194 | margin-bottom: 0.5rem; 195 | margin-left: 0; 196 | } 197 | 198 | blockquote { 199 | margin: 0 0 1rem; 200 | } 201 | 202 | b, 203 | strong { 204 | font-weight: bolder; 205 | } 206 | 207 | small { 208 | font-size: 0.875em; 209 | } 210 | 211 | mark { 212 | padding: 0.2em; 213 | background-color: #fcf8e3; 214 | } 215 | 216 | sub, 217 | sup { 218 | position: relative; 219 | font-size: 0.75em; 220 | line-height: 0; 221 | vertical-align: baseline; 222 | } 223 | 224 | sub { 225 | bottom: -0.25em; 226 | } 227 | 228 | sup { 229 | top: -0.5em; 230 | } 231 | 232 | a { 233 | color: #0d6efd; 234 | text-decoration: underline; 235 | } 236 | a:hover { 237 | color: #0a58ca; 238 | } 239 | 240 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 241 | color: inherit; 242 | text-decoration: none; 243 | } 244 | 245 | pre, 246 | code, 247 | kbd, 248 | samp { 249 | font-family: var(--bs-font-monospace); 250 | font-size: 1em; 251 | direction: ltr /* rtl:ignore */; 252 | unicode-bidi: bidi-override; 253 | } 254 | 255 | pre { 256 | display: block; 257 | margin-top: 0; 258 | margin-bottom: 1rem; 259 | overflow: auto; 260 | font-size: 0.875em; 261 | } 262 | pre code { 263 | font-size: inherit; 264 | color: inherit; 265 | word-break: normal; 266 | } 267 | 268 | code { 269 | font-size: 0.875em; 270 | color: #d63384; 271 | word-wrap: break-word; 272 | } 273 | a > code { 274 | color: inherit; 275 | } 276 | 277 | kbd { 278 | padding: 0.2rem 0.4rem; 279 | font-size: 0.875em; 280 | color: #fff; 281 | background-color: #212529; 282 | border-radius: 0.2rem; 283 | } 284 | kbd kbd { 285 | padding: 0; 286 | font-size: 1em; 287 | font-weight: 700; 288 | } 289 | 290 | figure { 291 | margin: 0 0 1rem; 292 | } 293 | 294 | img, 295 | svg { 296 | vertical-align: middle; 297 | } 298 | 299 | table { 300 | caption-side: bottom; 301 | border-collapse: collapse; 302 | } 303 | 304 | caption { 305 | padding-top: 0.5rem; 306 | padding-bottom: 0.5rem; 307 | color: #6c757d; 308 | text-align: left; 309 | } 310 | 311 | th { 312 | text-align: inherit; 313 | text-align: -webkit-match-parent; 314 | } 315 | 316 | thead, 317 | tbody, 318 | tfoot, 319 | tr, 320 | td, 321 | th { 322 | border-color: inherit; 323 | border-style: solid; 324 | border-width: 0; 325 | } 326 | 327 | label { 328 | display: inline-block; 329 | } 330 | 331 | button { 332 | border-radius: 0; 333 | } 334 | 335 | button:focus:not(:focus-visible) { 336 | outline: 0; 337 | } 338 | 339 | input, 340 | button, 341 | select, 342 | optgroup, 343 | textarea { 344 | margin: 0; 345 | font-family: inherit; 346 | font-size: inherit; 347 | line-height: inherit; 348 | } 349 | 350 | button, 351 | select { 352 | text-transform: none; 353 | } 354 | 355 | [role=button] { 356 | cursor: pointer; 357 | } 358 | 359 | select { 360 | word-wrap: normal; 361 | } 362 | select:disabled { 363 | opacity: 1; 364 | } 365 | 366 | [list]::-webkit-calendar-picker-indicator { 367 | display: none; 368 | } 369 | 370 | button, 371 | [type=button], 372 | [type=reset], 373 | [type=submit] { 374 | -webkit-appearance: button; 375 | } 376 | button:not(:disabled), 377 | [type=button]:not(:disabled), 378 | [type=reset]:not(:disabled), 379 | [type=submit]:not(:disabled) { 380 | cursor: pointer; 381 | } 382 | 383 | ::-moz-focus-inner { 384 | padding: 0; 385 | border-style: none; 386 | } 387 | 388 | textarea { 389 | resize: vertical; 390 | } 391 | 392 | fieldset { 393 | min-width: 0; 394 | padding: 0; 395 | margin: 0; 396 | border: 0; 397 | } 398 | 399 | legend { 400 | float: left; 401 | width: 100%; 402 | padding: 0; 403 | margin-bottom: 0.5rem; 404 | font-size: calc(1.275rem + 0.3vw); 405 | line-height: inherit; 406 | } 407 | @media (min-width: 1200px) { 408 | legend { 409 | font-size: 1.5rem; 410 | } 411 | } 412 | legend + * { 413 | clear: left; 414 | } 415 | 416 | ::-webkit-datetime-edit-fields-wrapper, 417 | ::-webkit-datetime-edit-text, 418 | ::-webkit-datetime-edit-minute, 419 | ::-webkit-datetime-edit-hour-field, 420 | ::-webkit-datetime-edit-day-field, 421 | ::-webkit-datetime-edit-month-field, 422 | ::-webkit-datetime-edit-year-field { 423 | padding: 0; 424 | } 425 | 426 | ::-webkit-inner-spin-button { 427 | height: auto; 428 | } 429 | 430 | [type=search] { 431 | outline-offset: -2px; 432 | -webkit-appearance: textfield; 433 | } 434 | 435 | /* rtl:raw: 436 | [type="tel"], 437 | [type="url"], 438 | [type="email"], 439 | [type="number"] { 440 | direction: ltr; 441 | } 442 | */ 443 | ::-webkit-search-decoration { 444 | -webkit-appearance: none; 445 | } 446 | 447 | ::-webkit-color-swatch-wrapper { 448 | padding: 0; 449 | } 450 | 451 | ::-webkit-file-upload-button { 452 | font: inherit; 453 | } 454 | 455 | ::file-selector-button { 456 | font: inherit; 457 | } 458 | 459 | ::-webkit-file-upload-button { 460 | font: inherit; 461 | -webkit-appearance: button; 462 | } 463 | 464 | output { 465 | display: inline-block; 466 | } 467 | 468 | iframe { 469 | border: 0; 470 | } 471 | 472 | summary { 473 | display: list-item; 474 | cursor: pointer; 475 | } 476 | 477 | progress { 478 | vertical-align: baseline; 479 | } 480 | 481 | [hidden] { 482 | display: none !important; 483 | } 484 | 485 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /landing/static/landing/vendor/bootstrap/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /landing/static/landing/vendor/bootstrap/css/bootstrap-reboot.rtl.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | :root { 9 | --bs-blue: #0d6efd; 10 | --bs-indigo: #6610f2; 11 | --bs-purple: #6f42c1; 12 | --bs-pink: #d63384; 13 | --bs-red: #dc3545; 14 | --bs-orange: #fd7e14; 15 | --bs-yellow: #ffc107; 16 | --bs-green: #198754; 17 | --bs-teal: #20c997; 18 | --bs-cyan: #0dcaf0; 19 | --bs-white: #fff; 20 | --bs-gray: #6c757d; 21 | --bs-gray-dark: #343a40; 22 | --bs-gray-100: #f8f9fa; 23 | --bs-gray-200: #e9ecef; 24 | --bs-gray-300: #dee2e6; 25 | --bs-gray-400: #ced4da; 26 | --bs-gray-500: #adb5bd; 27 | --bs-gray-600: #6c757d; 28 | --bs-gray-700: #495057; 29 | --bs-gray-800: #343a40; 30 | --bs-gray-900: #212529; 31 | --bs-primary: #0d6efd; 32 | --bs-secondary: #6c757d; 33 | --bs-success: #198754; 34 | --bs-info: #0dcaf0; 35 | --bs-warning: #ffc107; 36 | --bs-danger: #dc3545; 37 | --bs-light: #f8f9fa; 38 | --bs-dark: #212529; 39 | --bs-primary-rgb: 13, 110, 253; 40 | --bs-secondary-rgb: 108, 117, 125; 41 | --bs-success-rgb: 25, 135, 84; 42 | --bs-info-rgb: 13, 202, 240; 43 | --bs-warning-rgb: 255, 193, 7; 44 | --bs-danger-rgb: 220, 53, 69; 45 | --bs-light-rgb: 248, 249, 250; 46 | --bs-dark-rgb: 33, 37, 41; 47 | --bs-white-rgb: 255, 255, 255; 48 | --bs-black-rgb: 0, 0, 0; 49 | --bs-body-color-rgb: 33, 37, 41; 50 | --bs-body-bg-rgb: 255, 255, 255; 51 | --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 52 | --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 53 | --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); 54 | --bs-body-font-family: var(--bs-font-sans-serif); 55 | --bs-body-font-size: 1rem; 56 | --bs-body-font-weight: 400; 57 | --bs-body-line-height: 1.5; 58 | --bs-body-color: #212529; 59 | --bs-body-bg: #fff; 60 | } 61 | 62 | *, 63 | *::before, 64 | *::after { 65 | box-sizing: border-box; 66 | } 67 | 68 | @media (prefers-reduced-motion: no-preference) { 69 | :root { 70 | scroll-behavior: smooth; 71 | } 72 | } 73 | 74 | body { 75 | margin: 0; 76 | font-family: var(--bs-body-font-family); 77 | font-size: var(--bs-body-font-size); 78 | font-weight: var(--bs-body-font-weight); 79 | line-height: var(--bs-body-line-height); 80 | color: var(--bs-body-color); 81 | text-align: var(--bs-body-text-align); 82 | background-color: var(--bs-body-bg); 83 | -webkit-text-size-adjust: 100%; 84 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 85 | } 86 | 87 | hr { 88 | margin: 1rem 0; 89 | color: inherit; 90 | background-color: currentColor; 91 | border: 0; 92 | opacity: 0.25; 93 | } 94 | 95 | hr:not([size]) { 96 | height: 1px; 97 | } 98 | 99 | h6, h5, h4, h3, h2, h1 { 100 | margin-top: 0; 101 | margin-bottom: 0.5rem; 102 | font-weight: 500; 103 | line-height: 1.2; 104 | } 105 | 106 | h1 { 107 | font-size: calc(1.375rem + 1.5vw); 108 | } 109 | @media (min-width: 1200px) { 110 | h1 { 111 | font-size: 2.5rem; 112 | } 113 | } 114 | 115 | h2 { 116 | font-size: calc(1.325rem + 0.9vw); 117 | } 118 | @media (min-width: 1200px) { 119 | h2 { 120 | font-size: 2rem; 121 | } 122 | } 123 | 124 | h3 { 125 | font-size: calc(1.3rem + 0.6vw); 126 | } 127 | @media (min-width: 1200px) { 128 | h3 { 129 | font-size: 1.75rem; 130 | } 131 | } 132 | 133 | h4 { 134 | font-size: calc(1.275rem + 0.3vw); 135 | } 136 | @media (min-width: 1200px) { 137 | h4 { 138 | font-size: 1.5rem; 139 | } 140 | } 141 | 142 | h5 { 143 | font-size: 1.25rem; 144 | } 145 | 146 | h6 { 147 | font-size: 1rem; 148 | } 149 | 150 | p { 151 | margin-top: 0; 152 | margin-bottom: 1rem; 153 | } 154 | 155 | abbr[title], 156 | abbr[data-bs-original-title] { 157 | -webkit-text-decoration: underline dotted; 158 | text-decoration: underline dotted; 159 | cursor: help; 160 | -webkit-text-decoration-skip-ink: none; 161 | text-decoration-skip-ink: none; 162 | } 163 | 164 | address { 165 | margin-bottom: 1rem; 166 | font-style: normal; 167 | line-height: inherit; 168 | } 169 | 170 | ol, 171 | ul { 172 | padding-right: 2rem; 173 | } 174 | 175 | ol, 176 | ul, 177 | dl { 178 | margin-top: 0; 179 | margin-bottom: 1rem; 180 | } 181 | 182 | ol ol, 183 | ul ul, 184 | ol ul, 185 | ul ol { 186 | margin-bottom: 0; 187 | } 188 | 189 | dt { 190 | font-weight: 700; 191 | } 192 | 193 | dd { 194 | margin-bottom: 0.5rem; 195 | margin-right: 0; 196 | } 197 | 198 | blockquote { 199 | margin: 0 0 1rem; 200 | } 201 | 202 | b, 203 | strong { 204 | font-weight: bolder; 205 | } 206 | 207 | small { 208 | font-size: 0.875em; 209 | } 210 | 211 | mark { 212 | padding: 0.2em; 213 | background-color: #fcf8e3; 214 | } 215 | 216 | sub, 217 | sup { 218 | position: relative; 219 | font-size: 0.75em; 220 | line-height: 0; 221 | vertical-align: baseline; 222 | } 223 | 224 | sub { 225 | bottom: -0.25em; 226 | } 227 | 228 | sup { 229 | top: -0.5em; 230 | } 231 | 232 | a { 233 | color: #0d6efd; 234 | text-decoration: underline; 235 | } 236 | a:hover { 237 | color: #0a58ca; 238 | } 239 | 240 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 241 | color: inherit; 242 | text-decoration: none; 243 | } 244 | 245 | pre, 246 | code, 247 | kbd, 248 | samp { 249 | font-family: var(--bs-font-monospace); 250 | font-size: 1em; 251 | direction: ltr ; 252 | unicode-bidi: bidi-override; 253 | } 254 | 255 | pre { 256 | display: block; 257 | margin-top: 0; 258 | margin-bottom: 1rem; 259 | overflow: auto; 260 | font-size: 0.875em; 261 | } 262 | pre code { 263 | font-size: inherit; 264 | color: inherit; 265 | word-break: normal; 266 | } 267 | 268 | code { 269 | font-size: 0.875em; 270 | color: #d63384; 271 | word-wrap: break-word; 272 | } 273 | a > code { 274 | color: inherit; 275 | } 276 | 277 | kbd { 278 | padding: 0.2rem 0.4rem; 279 | font-size: 0.875em; 280 | color: #fff; 281 | background-color: #212529; 282 | border-radius: 0.2rem; 283 | } 284 | kbd kbd { 285 | padding: 0; 286 | font-size: 1em; 287 | font-weight: 700; 288 | } 289 | 290 | figure { 291 | margin: 0 0 1rem; 292 | } 293 | 294 | img, 295 | svg { 296 | vertical-align: middle; 297 | } 298 | 299 | table { 300 | caption-side: bottom; 301 | border-collapse: collapse; 302 | } 303 | 304 | caption { 305 | padding-top: 0.5rem; 306 | padding-bottom: 0.5rem; 307 | color: #6c757d; 308 | text-align: right; 309 | } 310 | 311 | th { 312 | text-align: inherit; 313 | text-align: -webkit-match-parent; 314 | } 315 | 316 | thead, 317 | tbody, 318 | tfoot, 319 | tr, 320 | td, 321 | th { 322 | border-color: inherit; 323 | border-style: solid; 324 | border-width: 0; 325 | } 326 | 327 | label { 328 | display: inline-block; 329 | } 330 | 331 | button { 332 | border-radius: 0; 333 | } 334 | 335 | button:focus:not(:focus-visible) { 336 | outline: 0; 337 | } 338 | 339 | input, 340 | button, 341 | select, 342 | optgroup, 343 | textarea { 344 | margin: 0; 345 | font-family: inherit; 346 | font-size: inherit; 347 | line-height: inherit; 348 | } 349 | 350 | button, 351 | select { 352 | text-transform: none; 353 | } 354 | 355 | [role=button] { 356 | cursor: pointer; 357 | } 358 | 359 | select { 360 | word-wrap: normal; 361 | } 362 | select:disabled { 363 | opacity: 1; 364 | } 365 | 366 | [list]::-webkit-calendar-picker-indicator { 367 | display: none; 368 | } 369 | 370 | button, 371 | [type=button], 372 | [type=reset], 373 | [type=submit] { 374 | -webkit-appearance: button; 375 | } 376 | button:not(:disabled), 377 | [type=button]:not(:disabled), 378 | [type=reset]:not(:disabled), 379 | [type=submit]:not(:disabled) { 380 | cursor: pointer; 381 | } 382 | 383 | ::-moz-focus-inner { 384 | padding: 0; 385 | border-style: none; 386 | } 387 | 388 | textarea { 389 | resize: vertical; 390 | } 391 | 392 | fieldset { 393 | min-width: 0; 394 | padding: 0; 395 | margin: 0; 396 | border: 0; 397 | } 398 | 399 | legend { 400 | float: right; 401 | width: 100%; 402 | padding: 0; 403 | margin-bottom: 0.5rem; 404 | font-size: calc(1.275rem + 0.3vw); 405 | line-height: inherit; 406 | } 407 | @media (min-width: 1200px) { 408 | legend { 409 | font-size: 1.5rem; 410 | } 411 | } 412 | legend + * { 413 | clear: right; 414 | } 415 | 416 | ::-webkit-datetime-edit-fields-wrapper, 417 | ::-webkit-datetime-edit-text, 418 | ::-webkit-datetime-edit-minute, 419 | ::-webkit-datetime-edit-hour-field, 420 | ::-webkit-datetime-edit-day-field, 421 | ::-webkit-datetime-edit-month-field, 422 | ::-webkit-datetime-edit-year-field { 423 | padding: 0; 424 | } 425 | 426 | ::-webkit-inner-spin-button { 427 | height: auto; 428 | } 429 | 430 | [type=search] { 431 | outline-offset: -2px; 432 | -webkit-appearance: textfield; 433 | } 434 | 435 | [type="tel"], 436 | [type="url"], 437 | [type="email"], 438 | [type="number"] { 439 | direction: ltr; 440 | } 441 | ::-webkit-search-decoration { 442 | -webkit-appearance: none; 443 | } 444 | 445 | ::-webkit-color-swatch-wrapper { 446 | padding: 0; 447 | } 448 | 449 | ::-webkit-file-upload-button { 450 | font: inherit; 451 | } 452 | 453 | ::file-selector-button { 454 | font: inherit; 455 | } 456 | 457 | ::-webkit-file-upload-button { 458 | font: inherit; 459 | -webkit-appearance: button; 460 | } 461 | 462 | output { 463 | display: inline-block; 464 | } 465 | 466 | iframe { 467 | border: 0; 468 | } 469 | 470 | summary { 471 | display: list-item; 472 | cursor: pointer; 473 | } 474 | 475 | progress { 476 | vertical-align: baseline; 477 | } 478 | 479 | [hidden] { 480 | display: none !important; 481 | } 482 | /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ -------------------------------------------------------------------------------- /landing/static/landing/vendor/bootstrap/css/bootstrap-reboot.rtl.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */ -------------------------------------------------------------------------------- /landing/static/landing/vendor/boxicons/css/animations.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes spin 2 | { 3 | 0% 4 | { 5 | -webkit-transform: rotate(0); 6 | transform: rotate(0); 7 | } 8 | 100% 9 | { 10 | -webkit-transform: rotate(359deg); 11 | transform: rotate(359deg); 12 | } 13 | } 14 | @keyframes spin 15 | { 16 | 0% 17 | { 18 | -webkit-transform: rotate(0); 19 | transform: rotate(0); 20 | } 21 | 100% 22 | { 23 | -webkit-transform: rotate(359deg); 24 | transform: rotate(359deg); 25 | } 26 | } 27 | @-webkit-keyframes burst 28 | { 29 | 0% 30 | { 31 | -webkit-transform: scale(1); 32 | transform: scale(1); 33 | 34 | opacity: 1; 35 | } 36 | 90% 37 | { 38 | -webkit-transform: scale(1.5); 39 | transform: scale(1.5); 40 | 41 | opacity: 0; 42 | } 43 | } 44 | @keyframes burst 45 | { 46 | 0% 47 | { 48 | -webkit-transform: scale(1); 49 | transform: scale(1); 50 | 51 | opacity: 1; 52 | } 53 | 90% 54 | { 55 | -webkit-transform: scale(1.5); 56 | transform: scale(1.5); 57 | 58 | opacity: 0; 59 | } 60 | } 61 | @-webkit-keyframes flashing 62 | { 63 | 0% 64 | { 65 | opacity: 1; 66 | } 67 | 45% 68 | { 69 | opacity: 0; 70 | } 71 | 90% 72 | { 73 | opacity: 1; 74 | } 75 | } 76 | @keyframes flashing 77 | { 78 | 0% 79 | { 80 | opacity: 1; 81 | } 82 | 45% 83 | { 84 | opacity: 0; 85 | } 86 | 90% 87 | { 88 | opacity: 1; 89 | } 90 | } 91 | @-webkit-keyframes fade-left 92 | { 93 | 0% 94 | { 95 | -webkit-transform: translateX(0); 96 | transform: translateX(0); 97 | 98 | opacity: 1; 99 | } 100 | 75% 101 | { 102 | -webkit-transform: translateX(-20px); 103 | transform: translateX(-20px); 104 | 105 | opacity: 0; 106 | } 107 | } 108 | @keyframes fade-left 109 | { 110 | 0% 111 | { 112 | -webkit-transform: translateX(0); 113 | transform: translateX(0); 114 | 115 | opacity: 1; 116 | } 117 | 75% 118 | { 119 | -webkit-transform: translateX(-20px); 120 | transform: translateX(-20px); 121 | 122 | opacity: 0; 123 | } 124 | } 125 | @-webkit-keyframes fade-right 126 | { 127 | 0% 128 | { 129 | -webkit-transform: translateX(0); 130 | transform: translateX(0); 131 | 132 | opacity: 1; 133 | } 134 | 75% 135 | { 136 | -webkit-transform: translateX(20px); 137 | transform: translateX(20px); 138 | 139 | opacity: 0; 140 | } 141 | } 142 | @keyframes fade-right 143 | { 144 | 0% 145 | { 146 | -webkit-transform: translateX(0); 147 | transform: translateX(0); 148 | 149 | opacity: 1; 150 | } 151 | 75% 152 | { 153 | -webkit-transform: translateX(20px); 154 | transform: translateX(20px); 155 | 156 | opacity: 0; 157 | } 158 | } 159 | @-webkit-keyframes fade-up 160 | { 161 | 0% 162 | { 163 | -webkit-transform: translateY(0); 164 | transform: translateY(0); 165 | 166 | opacity: 1; 167 | } 168 | 75% 169 | { 170 | -webkit-transform: translateY(-20px); 171 | transform: translateY(-20px); 172 | 173 | opacity: 0; 174 | } 175 | } 176 | @keyframes fade-up 177 | { 178 | 0% 179 | { 180 | -webkit-transform: translateY(0); 181 | transform: translateY(0); 182 | 183 | opacity: 1; 184 | } 185 | 75% 186 | { 187 | -webkit-transform: translateY(-20px); 188 | transform: translateY(-20px); 189 | 190 | opacity: 0; 191 | } 192 | } 193 | @-webkit-keyframes fade-down 194 | { 195 | 0% 196 | { 197 | -webkit-transform: translateY(0); 198 | transform: translateY(0); 199 | 200 | opacity: 1; 201 | } 202 | 75% 203 | { 204 | -webkit-transform: translateY(20px); 205 | transform: translateY(20px); 206 | 207 | opacity: 0; 208 | } 209 | } 210 | @keyframes fade-down 211 | { 212 | 0% 213 | { 214 | -webkit-transform: translateY(0); 215 | transform: translateY(0); 216 | 217 | opacity: 1; 218 | } 219 | 75% 220 | { 221 | -webkit-transform: translateY(20px); 222 | transform: translateY(20px); 223 | 224 | opacity: 0; 225 | } 226 | } 227 | @-webkit-keyframes tada 228 | { 229 | from 230 | { 231 | -webkit-transform: scale3d(1, 1, 1); 232 | transform: scale3d(1, 1, 1); 233 | } 234 | 235 | 10%, 236 | 20% 237 | { 238 | -webkit-transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg); 239 | transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg); 240 | } 241 | 242 | 30%, 243 | 50%, 244 | 70%, 245 | 90% 246 | { 247 | -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg); 248 | transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg); 249 | } 250 | 251 | 40%, 252 | 60%, 253 | 80% 254 | { 255 | -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, -10deg); 256 | transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, -10deg); 257 | } 258 | 259 | to 260 | { 261 | -webkit-transform: scale3d(1, 1, 1); 262 | transform: scale3d(1, 1, 1); 263 | } 264 | } 265 | 266 | @keyframes tada 267 | { 268 | from 269 | { 270 | -webkit-transform: scale3d(1, 1, 1); 271 | transform: scale3d(1, 1, 1); 272 | } 273 | 274 | 10%, 275 | 20% 276 | { 277 | -webkit-transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg); 278 | transform: scale3d(.95, .95, .95) rotate3d(0, 0, 1, -10deg); 279 | } 280 | 281 | 30%, 282 | 50%, 283 | 70%, 284 | 90% 285 | { 286 | -webkit-transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg); 287 | transform: scale3d(1, 1, 1) rotate3d(0, 0, 1, 10deg); 288 | } 289 | 290 | 40%, 291 | 60%, 292 | 80% 293 | { 294 | -webkit-transform: rotate3d(0, 0, 1, -10deg); 295 | transform: rotate3d(0, 0, 1, -10deg); 296 | } 297 | 298 | to 299 | { 300 | -webkit-transform: scale3d(1, 1, 1); 301 | transform: scale3d(1, 1, 1); 302 | } 303 | } 304 | .bx-spin 305 | { 306 | -webkit-animation: spin 2s linear infinite; 307 | animation: spin 2s linear infinite; 308 | } 309 | .bx-spin-hover:hover 310 | { 311 | -webkit-animation: spin 2s linear infinite; 312 | animation: spin 2s linear infinite; 313 | } 314 | 315 | .bx-tada 316 | { 317 | -webkit-animation: tada 1.5s ease infinite; 318 | animation: tada 1.5s ease infinite; 319 | } 320 | .bx-tada-hover:hover 321 | { 322 | -webkit-animation: tada 1.5s ease infinite; 323 | animation: tada 1.5s ease infinite; 324 | } 325 | 326 | .bx-flashing 327 | { 328 | -webkit-animation: flashing 1.5s infinite linear; 329 | animation: flashing 1.5s infinite linear; 330 | } 331 | .bx-flashing-hover:hover 332 | { 333 | -webkit-animation: flashing 1.5s infinite linear; 334 | animation: flashing 1.5s infinite linear; 335 | } 336 | 337 | .bx-burst 338 | { 339 | -webkit-animation: burst 1.5s infinite linear; 340 | animation: burst 1.5s infinite linear; 341 | } 342 | .bx-burst-hover:hover 343 | { 344 | -webkit-animation: burst 1.5s infinite linear; 345 | animation: burst 1.5s infinite linear; 346 | } 347 | .bx-fade-up 348 | { 349 | -webkit-animation: fade-up 1.5s infinite linear; 350 | animation: fade-up 1.5s infinite linear; 351 | } 352 | .bx-fade-up-hover:hover 353 | { 354 | -webkit-animation: fade-up 1.5s infinite linear; 355 | animation: fade-up 1.5s infinite linear; 356 | } 357 | .bx-fade-down 358 | { 359 | -webkit-animation: fade-down 1.5s infinite linear; 360 | animation: fade-down 1.5s infinite linear; 361 | } 362 | .bx-fade-down-hover:hover 363 | { 364 | -webkit-animation: fade-down 1.5s infinite linear; 365 | animation: fade-down 1.5s infinite linear; 366 | } 367 | .bx-fade-left 368 | { 369 | -webkit-animation: fade-left 1.5s infinite linear; 370 | animation: fade-left 1.5s infinite linear; 371 | } 372 | .bx-fade-left-hover:hover 373 | { 374 | -webkit-animation: fade-left 1.5s infinite linear; 375 | animation: fade-left 1.5s infinite linear; 376 | } 377 | .bx-fade-right 378 | { 379 | -webkit-animation: fade-right 1.5s infinite linear; 380 | animation: fade-right 1.5s infinite linear; 381 | } 382 | .bx-fade-right-hover:hover 383 | { 384 | -webkit-animation: fade-right 1.5s infinite linear; 385 | animation: fade-right 1.5s infinite linear; 386 | } -------------------------------------------------------------------------------- /landing/static/landing/vendor/boxicons/css/transformations.css: -------------------------------------------------------------------------------- 1 | .bx-rotate-90 2 | { 3 | transform: rotate(90deg); 4 | 5 | -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=1)'; 6 | } 7 | .bx-rotate-180 8 | { 9 | transform: rotate(180deg); 10 | 11 | -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2)'; 12 | } 13 | .bx-rotate-270 14 | { 15 | transform: rotate(270deg); 16 | 17 | -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=3)'; 18 | } 19 | .bx-flip-horizontal 20 | { 21 | transform: scaleX(-1); 22 | 23 | -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)'; 24 | } 25 | .bx-flip-vertical 26 | { 27 | transform: scaleY(-1); 28 | 29 | -ms-filter: 'progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)'; 30 | } 31 | -------------------------------------------------------------------------------- /landing/static/landing/vendor/boxicons/fonts/boxicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/boxicons/fonts/boxicons.eot -------------------------------------------------------------------------------- /landing/static/landing/vendor/boxicons/fonts/boxicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/boxicons/fonts/boxicons.ttf -------------------------------------------------------------------------------- /landing/static/landing/vendor/boxicons/fonts/boxicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/boxicons/fonts/boxicons.woff -------------------------------------------------------------------------------- /landing/static/landing/vendor/boxicons/fonts/boxicons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/boxicons/fonts/boxicons.woff2 -------------------------------------------------------------------------------- /landing/static/landing/vendor/glightbox/css/glightbox.min.css: -------------------------------------------------------------------------------- 1 | .glightbox-container{width:100%;height:100%;position:fixed;top:0;left:0;z-index:999999!important;overflow:hidden;-ms-touch-action:none;touch-action:none;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;outline:0;overflow:hidden}.glightbox-container.inactive{display:none}.glightbox-container .gcontainer{position:relative;width:100%;height:100%;z-index:9999;overflow:hidden}.glightbox-container .gslider{-webkit-transition:-webkit-transform .4s ease;transition:-webkit-transform .4s ease;transition:transform .4s ease;transition:transform .4s ease,-webkit-transform .4s ease;height:100%;left:0;top:0;width:100%;position:relative;overflow:hidden;display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.glightbox-container .gslide{width:100%;position:absolute;opacity:1;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;opacity:0}.glightbox-container .gslide.current{opacity:1;z-index:99999;position:relative}.glightbox-container .gslide.prev{opacity:1;z-index:9999}.glightbox-container .gslide-inner-content{width:100%}.glightbox-container .ginner-container{position:relative;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;max-width:100%;margin:auto;height:100vh}.glightbox-container .ginner-container.gvideo-container{width:100%}.glightbox-container .ginner-container.desc-bottom,.glightbox-container .ginner-container.desc-top{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.glightbox-container .ginner-container.desc-left,.glightbox-container .ginner-container.desc-right{max-width:100%!important}.gslide iframe,.gslide video{outline:0!important;border:none;min-height:165px;-webkit-overflow-scrolling:touch;-ms-touch-action:auto;touch-action:auto}.gslide:not(.current){pointer-events:none}.gslide-image{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.gslide-image img{max-height:100vh;display:block;padding:0;float:none;outline:0;border:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;max-width:100vw;width:auto;height:auto;-o-object-fit:cover;object-fit:cover;-ms-touch-action:none;touch-action:none;margin:auto;min-width:200px}.desc-bottom .gslide-image img,.desc-top .gslide-image img{width:auto}.desc-left .gslide-image img,.desc-right .gslide-image img{width:auto;max-width:100%}.gslide-image img.zoomable{position:relative}.gslide-image img.dragging{cursor:-webkit-grabbing!important;cursor:grabbing!important;-webkit-transition:none;transition:none}.gslide-video{position:relative;max-width:100vh;width:100%!important}.gslide-video .gvideo-wrapper{width:100%;margin:auto}.gslide-video::before{content:'';display:block;position:absolute;width:100%;height:100%;background:rgba(255,0,0,.34);display:none}.gslide-video.playing::before{display:none}.gslide-video.fullscreen{max-width:100%!important;min-width:100%;height:75vh}.gslide-video.fullscreen video{max-width:100%!important;width:100%!important}.gslide-inline{background:#fff;text-align:left;max-height:calc(100vh - 40px);overflow:auto;max-width:100%}.gslide-inline .ginlined-content{padding:20px;width:100%}.gslide-inline .dragging{cursor:-webkit-grabbing!important;cursor:grabbing!important;-webkit-transition:none;transition:none}.ginlined-content{overflow:auto;display:block!important;opacity:1}.gslide-external{display:-webkit-box;display:-ms-flexbox;display:flex;width:100%;min-width:100%;background:#fff;padding:0;overflow:auto;max-height:75vh;height:100%}.gslide-media{display:-webkit-box;display:-ms-flexbox;display:flex;width:auto}.zoomed .gslide-media{-webkit-box-shadow:none!important;box-shadow:none!important}.desc-bottom .gslide-media,.desc-top .gslide-media{margin:0 auto;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gslide-description{position:relative;-webkit-box-flex:1;-ms-flex:1 0 100%;flex:1 0 100%}.gslide-description.description-left,.gslide-description.description-right{max-width:100%}.gslide-description.description-bottom,.gslide-description.description-top{margin:0 auto;width:100%}.gslide-description p{margin-bottom:12px}.gslide-description p:last-child{margin-bottom:0}.zoomed .gslide-description{display:none}.glightbox-button-hidden{display:none}.glightbox-mobile .glightbox-container .gslide-description{height:auto!important;width:100%;background:0 0;position:absolute;bottom:0;padding:19px 11px;max-width:100vw!important;-webkit-box-ordinal-group:3!important;-ms-flex-order:2!important;order:2!important;max-height:78vh;overflow:auto!important;background:-webkit-gradient(linear,left top,left bottom,from(rgba(0,0,0,0)),to(rgba(0,0,0,.75)));background:linear-gradient(to bottom,rgba(0,0,0,0) 0,rgba(0,0,0,.75) 100%);-webkit-transition:opacity .3s linear;transition:opacity .3s linear;padding-bottom:50px}.glightbox-mobile .glightbox-container .gslide-title{color:#fff;font-size:1em}.glightbox-mobile .glightbox-container .gslide-desc{color:#a1a1a1}.glightbox-mobile .glightbox-container .gslide-desc a{color:#fff;font-weight:700}.glightbox-mobile .glightbox-container .gslide-desc *{color:inherit}.glightbox-mobile .glightbox-container .gslide-desc string{color:#fff}.glightbox-mobile .glightbox-container .gslide-desc .desc-more{color:#fff;opacity:.4}.gdesc-open .gslide-media{-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:.4}.gdesc-open .gdesc-inner{padding-bottom:30px}.gdesc-closed .gslide-media{-webkit-transition:opacity .5s ease;transition:opacity .5s ease;opacity:1}.greset{-webkit-transition:all .3s ease;transition:all .3s ease}.gabsolute{position:absolute}.grelative{position:relative}.glightbox-desc{display:none!important}.glightbox-open{overflow:hidden}.gloader{height:25px;width:25px;-webkit-animation:lightboxLoader .8s infinite linear;animation:lightboxLoader .8s infinite linear;border:2px solid #fff;border-right-color:transparent;border-radius:50%;position:absolute;display:block;z-index:9999;left:0;right:0;margin:0 auto;top:47%}.goverlay{width:100%;height:calc(100vh + 1px);position:fixed;top:-1px;left:0;background:#000;will-change:opacity}.glightbox-mobile .goverlay{background:#000}.gclose,.gnext,.gprev{z-index:99999;cursor:pointer;width:26px;height:44px;border:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gclose svg,.gnext svg,.gprev svg{display:block;width:25px;height:auto;margin:0;padding:0}.gclose.disabled,.gnext.disabled,.gprev.disabled{opacity:.1}.gclose .garrow,.gnext .garrow,.gprev .garrow{stroke:#fff}.gbtn.focused{outline:2px solid #0f3d81}iframe.wait-autoplay{opacity:0}.glightbox-closing .gclose,.glightbox-closing .gnext,.glightbox-closing .gprev{opacity:0!important}.glightbox-clean .gslide-description{background:#fff}.glightbox-clean .gdesc-inner{padding:22px 20px}.glightbox-clean .gslide-title{font-size:1em;font-weight:400;font-family:arial;color:#000;margin-bottom:19px;line-height:1.4em}.glightbox-clean .gslide-desc{font-size:.86em;margin-bottom:0;font-family:arial;line-height:1.4em}.glightbox-clean .gslide-video{background:#000}.glightbox-clean .gclose,.glightbox-clean .gnext,.glightbox-clean .gprev{background-color:rgba(0,0,0,.75);border-radius:4px}.glightbox-clean .gclose path,.glightbox-clean .gnext path,.glightbox-clean .gprev path{fill:#fff}.glightbox-clean .gprev{position:absolute;top:-100%;left:30px;width:40px;height:50px}.glightbox-clean .gnext{position:absolute;top:-100%;right:30px;width:40px;height:50px}.glightbox-clean .gclose{width:35px;height:35px;top:15px;right:10px;position:absolute}.glightbox-clean .gclose svg{width:18px;height:auto}.glightbox-clean .gclose:hover{opacity:1}.gfadeIn{-webkit-animation:gfadeIn .5s ease;animation:gfadeIn .5s ease}.gfadeOut{-webkit-animation:gfadeOut .5s ease;animation:gfadeOut .5s ease}.gslideOutLeft{-webkit-animation:gslideOutLeft .3s ease;animation:gslideOutLeft .3s ease}.gslideInLeft{-webkit-animation:gslideInLeft .3s ease;animation:gslideInLeft .3s ease}.gslideOutRight{-webkit-animation:gslideOutRight .3s ease;animation:gslideOutRight .3s ease}.gslideInRight{-webkit-animation:gslideInRight .3s ease;animation:gslideInRight .3s ease}.gzoomIn{-webkit-animation:gzoomIn .5s ease;animation:gzoomIn .5s ease}.gzoomOut{-webkit-animation:gzoomOut .5s ease;animation:gzoomOut .5s ease}@-webkit-keyframes lightboxLoader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes lightboxLoader{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-webkit-keyframes gfadeIn{from{opacity:0}to{opacity:1}}@keyframes gfadeIn{from{opacity:0}to{opacity:1}}@-webkit-keyframes gfadeOut{from{opacity:1}to{opacity:0}}@keyframes gfadeOut{from{opacity:1}to{opacity:0}}@-webkit-keyframes gslideInLeft{from{opacity:0;-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0)}to{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes gslideInLeft{from{opacity:0;-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0)}to{visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes gslideOutLeft{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0);opacity:0;visibility:hidden}}@keyframes gslideOutLeft{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(-60%,0,0);transform:translate3d(-60%,0,0);opacity:0;visibility:hidden}}@-webkit-keyframes gslideInRight{from{opacity:0;visibility:visible;-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@keyframes gslideInRight{from{opacity:0;visibility:visible;-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0)}to{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}}@-webkit-keyframes gslideOutRight{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0);opacity:0}}@keyframes gslideOutRight{from{opacity:1;visibility:visible;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}to{-webkit-transform:translate3d(60%,0,0);transform:translate3d(60%,0,0);opacity:0}}@-webkit-keyframes gzoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:1}}@keyframes gzoomIn{from{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:1}}@-webkit-keyframes gzoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes gzoomOut{from{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@media (min-width:769px){.glightbox-container .ginner-container{width:auto;height:auto;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.glightbox-container .ginner-container.desc-top .gslide-description{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.glightbox-container .ginner-container.desc-top .gslide-image,.glightbox-container .ginner-container.desc-top .gslide-image img{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.glightbox-container .ginner-container.desc-left .gslide-description{-webkit-box-ordinal-group:1;-ms-flex-order:0;order:0}.glightbox-container .ginner-container.desc-left .gslide-image{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.gslide-image img{max-height:97vh;max-width:100%}.gslide-image img.zoomable{cursor:-webkit-zoom-in;cursor:zoom-in}.zoomed .gslide-image img.zoomable{cursor:-webkit-grab;cursor:grab}.gslide-inline{max-height:95vh}.gslide-external{max-height:100vh}.gslide-description.description-left,.gslide-description.description-right{max-width:275px}.glightbox-open{height:auto}.goverlay{background:rgba(0,0,0,.92)}.glightbox-clean .gslide-media{-webkit-box-shadow:1px 2px 9px 0 rgba(0,0,0,.65);box-shadow:1px 2px 9px 0 rgba(0,0,0,.65)}.glightbox-clean .description-left .gdesc-inner,.glightbox-clean .description-right .gdesc-inner{position:absolute;height:100%;overflow-y:auto}.glightbox-clean .gclose,.glightbox-clean .gnext,.glightbox-clean .gprev{background-color:rgba(0,0,0,.32)}.glightbox-clean .gclose:hover,.glightbox-clean .gnext:hover,.glightbox-clean .gprev:hover{background-color:rgba(0,0,0,.7)}.glightbox-clean .gprev{top:45%}.glightbox-clean .gnext{top:45%}}@media (min-width:992px){.glightbox-clean .gclose{opacity:.7;right:20px}}@media screen and (max-height:420px){.goverlay{background:#000}} -------------------------------------------------------------------------------- /landing/static/landing/vendor/remixicon/remixicon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/remixicon/remixicon.eot -------------------------------------------------------------------------------- /landing/static/landing/vendor/remixicon/remixicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/remixicon/remixicon.ttf -------------------------------------------------------------------------------- /landing/static/landing/vendor/remixicon/remixicon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/remixicon/remixicon.woff -------------------------------------------------------------------------------- /landing/static/landing/vendor/remixicon/remixicon.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/landing/static/landing/vendor/remixicon/remixicon.woff2 -------------------------------------------------------------------------------- /landing/static/landing/vendor/swiper/swiper-bundle.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Swiper 7.3.0 3 | * Most modern mobile touch slider and framework with hardware accelerated transitions 4 | * https://swiperjs.com 5 | * 6 | * Copyright 2014-2021 Vladimir Kharlampidi 7 | * 8 | * Released under the MIT License 9 | * 10 | * Released on: November 18, 2021 11 | */ 12 | 13 | @font-face{font-family:swiper-icons;src:url('data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA');font-weight:400;font-style:normal}:root{--swiper-theme-color:#007aff}.swiper{margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;box-sizing:content-box}.swiper-android .swiper-slide,.swiper-wrapper{transform:translate3d(0px,0,0)}.swiper-pointer-events{touch-action:pan-y}.swiper-pointer-events.swiper-vertical{touch-action:pan-x}.swiper-slide{flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-3d,.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d .swiper-slide,.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top,.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d .swiper-slide-shadow,.swiper-3d .swiper-slide-shadow-bottom,.swiper-3d .swiper-slide-shadow-left,.swiper-3d .swiper-slide-shadow-right,.swiper-3d .swiper-slide-shadow-top{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}.swiper-3d .swiper-slide-shadow{background:rgba(0,0,0,.15)}.swiper-3d .swiper-slide-shadow-left{background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-right{background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-top{background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-3d .swiper-slide-shadow-bottom{background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}.swiper-horizontal.swiper-css-mode>.swiper-wrapper{scroll-snap-type:x mandatory}.swiper-vertical.swiper-css-mode>.swiper-wrapper{scroll-snap-type:y mandatory}.swiper-centered>.swiper-wrapper::before{content:'';flex-shrink:0;order:9999}.swiper-centered.swiper-horizontal>.swiper-wrapper>.swiper-slide:first-child{margin-inline-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-horizontal>.swiper-wrapper::before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-centered.swiper-vertical>.swiper-wrapper>.swiper-slide:first-child{margin-block-start:var(--swiper-centered-offset-before)}.swiper-centered.swiper-vertical>.swiper-wrapper::before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}.swiper-centered>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center}.swiper-virtual.swiper-css-mode .swiper-wrapper::after{content:'';position:absolute;left:0;top:0;pointer-events:none}.swiper-virtual.swiper-css-mode.swiper-horizontal .swiper-wrapper::after{height:1px;width:var(--swiper-virtual-size)}.swiper-virtual.swiper-css-mode.swiper-vertical .swiper-wrapper::after{width:1px;height:var(--swiper-virtual-size)}:root{--swiper-navigation-size:44px}.swiper-button-next,.swiper-button-prev{position:absolute;top:50%;width:calc(var(--swiper-navigation-size)/ 44 * 27);height:var(--swiper-navigation-size);margin-top:calc(0px - (var(--swiper-navigation-size)/ 2));z-index:10;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--swiper-navigation-color,var(--swiper-theme-color))}.swiper-button-next.swiper-button-disabled,.swiper-button-prev.swiper-button-disabled{opacity:.35;cursor:auto;pointer-events:none}.swiper-button-next:after,.swiper-button-prev:after{font-family:swiper-icons;font-size:var(--swiper-navigation-size);text-transform:none!important;letter-spacing:0;text-transform:none;font-variant:initial;line-height:1}.swiper-button-prev,.swiper-rtl .swiper-button-next{left:10px;right:auto}.swiper-button-prev:after,.swiper-rtl .swiper-button-next:after{content:'prev'}.swiper-button-next,.swiper-rtl .swiper-button-prev{right:10px;left:auto}.swiper-button-next:after,.swiper-rtl .swiper-button-prev:after{content:'next'}.swiper-button-lock{display:none}.swiper-pagination{position:absolute;text-align:center;transition:.3s opacity;transform:translate3d(0,0,0);z-index:10}.swiper-pagination.swiper-pagination-hidden{opacity:0}.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-bullets.swiper-pagination-horizontal,.swiper-pagination-custom,.swiper-pagination-fraction{bottom:10px;left:0;width:100%}.swiper-pagination-bullets-dynamic{overflow:hidden;font-size:0}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transform:scale(.33);position:relative}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main{transform:scale(1)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev{transform:scale(.33)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next{transform:scale(.66)}.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next{transform:scale(.33)}.swiper-pagination-bullet{width:var(--swiper-pagination-bullet-width,var(--swiper-pagination-bullet-size,8px));height:var(--swiper-pagination-bullet-height,var(--swiper-pagination-bullet-size,8px));display:inline-block;border-radius:50%;background:var(--swiper-pagination-bullet-inactive-color,#000);opacity:var(--swiper-pagination-bullet-inactive-opacity, .2)}button.swiper-pagination-bullet{border:none;margin:0;padding:0;box-shadow:none;-webkit-appearance:none;appearance:none}.swiper-pagination-clickable .swiper-pagination-bullet{cursor:pointer}.swiper-pagination-bullet:only-child{display:none!important}.swiper-pagination-bullet-active{opacity:var(--swiper-pagination-bullet-opacity, 1);background:var(--swiper-pagination-color,var(--swiper-theme-color))}.swiper-pagination-vertical.swiper-pagination-bullets,.swiper-vertical>.swiper-pagination-bullets{right:10px;top:50%;transform:translate3d(0px,-50%,0)}.swiper-pagination-vertical.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets .swiper-pagination-bullet{margin:var(--swiper-pagination-bullet-vertical-gap,6px) 0;display:block}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{top:50%;transform:translateY(-50%);width:8px}.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-vertical>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{display:inline-block;transition:.2s transform,.2s top}.swiper-horizontal>.swiper-pagination-bullets .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet{margin:0 var(--swiper-pagination-bullet-horizontal-gap,4px)}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic{left:50%;transform:translateX(-50%);white-space:nowrap}.swiper-horizontal>.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s left}.swiper-horizontal.swiper-rtl>.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:.2s transform,.2s right}.swiper-pagination-progressbar{background:rgba(0,0,0,.25);position:absolute}.swiper-pagination-progressbar .swiper-pagination-progressbar-fill{background:var(--swiper-pagination-color,var(--swiper-theme-color));position:absolute;left:0;top:0;width:100%;height:100%;transform:scale(0);transform-origin:left top}.swiper-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill{transform-origin:right top}.swiper-horizontal>.swiper-pagination-progressbar,.swiper-pagination-progressbar.swiper-pagination-horizontal,.swiper-pagination-progressbar.swiper-pagination-vertical.swiper-pagination-progressbar-opposite,.swiper-vertical>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite{width:100%;height:4px;left:0;top:0}.swiper-horizontal>.swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite,.swiper-pagination-progressbar.swiper-pagination-vertical,.swiper-vertical>.swiper-pagination-progressbar{width:4px;height:100%;left:0;top:0}.swiper-pagination-lock{display:none}.swiper-scrollbar{border-radius:10px;position:relative;-ms-touch-action:none;background:rgba(0,0,0,.1)}.swiper-horizontal>.swiper-scrollbar{position:absolute;left:1%;bottom:3px;z-index:50;height:5px;width:98%}.swiper-vertical>.swiper-scrollbar{position:absolute;right:3px;top:1%;z-index:50;width:5px;height:98%}.swiper-scrollbar-drag{height:100%;width:100%;position:relative;background:rgba(0,0,0,.5);border-radius:10px;left:0;top:0}.swiper-scrollbar-cursor-drag{cursor:move}.swiper-scrollbar-lock{display:none}.swiper-zoom-container{width:100%;height:100%;display:flex;justify-content:center;align-items:center;text-align:center}.swiper-zoom-container>canvas,.swiper-zoom-container>img,.swiper-zoom-container>svg{max-width:100%;max-height:100%;object-fit:contain}.swiper-slide-zoomed{cursor:move}.swiper-lazy-preloader{width:42px;height:42px;position:absolute;left:50%;top:50%;margin-left:-21px;margin-top:-21px;z-index:10;transform-origin:50%;animation:swiper-preloader-spin 1s infinite linear;box-sizing:border-box;border:4px solid var(--swiper-preloader-color,var(--swiper-theme-color));border-radius:50%;border-top-color:transparent}.swiper-lazy-preloader-white{--swiper-preloader-color:#fff}.swiper-lazy-preloader-black{--swiper-preloader-color:#000}@keyframes swiper-preloader-spin{100%{transform:rotate(360deg)}}.swiper .swiper-notification{position:absolute;left:0;top:0;pointer-events:none;opacity:0;z-index:-1000}.swiper-free-mode>.swiper-wrapper{transition-timing-function:ease-out;margin:0 auto}.swiper-grid>.swiper-wrapper{flex-wrap:wrap}.swiper-grid-column>.swiper-wrapper{flex-wrap:wrap;flex-direction:column}.swiper-fade.swiper-free-mode .swiper-slide{transition-timing-function:ease-out}.swiper-fade .swiper-slide{pointer-events:none;transition-property:opacity}.swiper-fade .swiper-slide .swiper-slide{pointer-events:none}.swiper-fade .swiper-slide-active,.swiper-fade .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-cube{overflow:visible}.swiper-cube .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1;visibility:hidden;transform-origin:0 0;width:100%;height:100%}.swiper-cube .swiper-slide .swiper-slide{pointer-events:none}.swiper-cube.swiper-rtl .swiper-slide{transform-origin:100% 0}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-cube .swiper-slide-active,.swiper-cube .swiper-slide-next,.swiper-cube .swiper-slide-next+.swiper-slide,.swiper-cube .swiper-slide-prev{pointer-events:auto;visibility:visible}.swiper-cube .swiper-slide-shadow-bottom,.swiper-cube .swiper-slide-shadow-left,.swiper-cube .swiper-slide-shadow-right,.swiper-cube .swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-cube .swiper-cube-shadow{position:absolute;left:0;bottom:0px;width:100%;height:100%;opacity:.6;z-index:0}.swiper-cube .swiper-cube-shadow:before{content:'';background:#000;position:absolute;left:0;top:0;bottom:0;right:0;filter:blur(50px)}.swiper-flip{overflow:visible}.swiper-flip .swiper-slide{pointer-events:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;z-index:1}.swiper-flip .swiper-slide .swiper-slide{pointer-events:none}.swiper-flip .swiper-slide-active,.swiper-flip .swiper-slide-active .swiper-slide-active{pointer-events:auto}.swiper-flip .swiper-slide-shadow-bottom,.swiper-flip .swiper-slide-shadow-left,.swiper-flip .swiper-slide-shadow-right,.swiper-flip .swiper-slide-shadow-top{z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-creative .swiper-slide{-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden;transition-property:transform,opacity,height}.swiper-cards{overflow:visible}.swiper-cards .swiper-slide{transform-origin:center bottom;-webkit-backface-visibility:hidden;backface-visibility:hidden;overflow:hidden} -------------------------------------------------------------------------------- /landing/templates/landing/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | {% block title %} 8 | FaceFind 9 | {% endblock title%} 10 | 11 | {% block favicon %} 12 | 13 | {% endblock favicon %} 14 | 15 | {% block meta %} 16 | 17 | {% block meta_charset %} 18 | 19 | {% endblock meta_charset %} 20 | 21 | {% block meta_contentlanguage %} 22 | 23 | {% endblock meta_contentlanguage %} 24 | 25 | {% block meta_viewport %} 26 | 27 | {% endblock meta_viewport %} 28 | 29 | {% endblock meta %} 30 | 31 | 32 | {% block head %} 33 | {% load static %} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 66 | {% endblock head %} 67 | 68 | {% block style %} 69 | {% endblock style %} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 138 | 139 | 140 | {% block content %} 141 | 142 | {% endblock content %} 143 | 144 | 145 |
146 | 148 | 149 | 150 | {% block script %} 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | {% endblock script %} 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /landing/templates/landing/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 | 8 |
9 |
10 |
12 |

Even One Missing Person Is One Too Many!

13 |

FaceFind, firmly believes in that, and we are committed to change this by 14 | finding missing people using Computer Vision and Face 15 | Recognition Technologies.

16 |
17 | Get Started 18 | Demo Video 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 | 31 | 32 |
33 |
34 | 35 |
36 |

About Us

37 |
38 | 39 |
40 |
41 |

42 | The thought of a family member, a friend or someone else you care about 43 | going missing can be terrifying. 44 | We at FaceFind help find your loved ones using Face Recognition 45 | Technologies. If someone you know is missing, then, 46 |

47 |
    48 |
  • Register the missing person with 49 | us.
  • 50 |
  • We will contact you for a 51 | background check.
  • 52 |
  • Once verified, the missing 53 | person is added to our database of missing people.
  • 54 |
  • When volunteers report a 55 | suspected missing person, we 56 | use face recognition to identify a potential match with our 57 | database.
  • 58 |
  • If a match is found we will 59 | report to the local authorities and contact you.
  • 60 | 61 |
62 | Register a 63 | Missing Person 64 |
65 |
66 |

67 | It is estimated that a child goes missing in India every eight minutes. 68 | Millions end up in forced labour, domestic slavery and sex work, in 69 | what’s become a lucrative industry. 70 | 71 | If you ever suspect a person could have been trafficked this way, 72 |

73 |
    74 |
  • Report the suspected person. 75 |
  • 76 |
  • We will cross-verify the report 77 | to check its validity.
  • 78 |
  • Once verified, use face 79 | recognition to identify a 80 | potential match with our database of missing people.
  • 81 |
  • If a match is found we will 82 | report to the local authorities and contact the family/guardian. 83 |
  • 84 | 85 |
86 | Report a 87 | Suspected Missing Person 88 | 89 |
90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 | 98 |
99 |

Technology

100 |

To achieve our goal of finding missing people, we make use of the following 101 | technologies,

102 |
103 | 104 |
105 |
107 |
108 |
109 |

Bootstrap

110 |

A free and open-source framework of HTML, CSS and JavaScript, 111 | directed at responsive, 112 | mobile-first front-end web development.

113 |
114 |
115 | 116 |
118 |
119 |
120 |

Django

121 |

A free and open-source, high-level Python based web framework 122 | that enables rapid development of secure and maintainable 123 | websites.

124 |
125 |
126 | 127 |
129 |
130 |
131 |

Azure 132 | Face API

133 |

An AI service by Microsoft that can detect, identify and analyse 134 | faces in images and videos.

135 |
136 |
137 | 138 |
140 |
141 |
142 |

SQLite

143 |

An in-process library that implements a self-contained, serverless, 144 | zero-configuration, transactional SQL database engine.

145 |
146 |
147 | 148 |
149 | 150 |
151 |
152 | 153 |
154 |
155 | 156 |
157 |

Frequently Asked Questions

158 |
159 | 160 |
161 |
    162 |
  • 163 | What happens after 165 | I register a Missing Person? 168 |
    169 |

    170 | Once you have successfully registered a missing person, 171 | we will contact you for a background check followed by 172 | which, 173 | we generate a unique Face ID for the missing person 174 | using Azure's Face API. 175 | When volunteers report a suspected missing person, we 176 | verify and generate a Face ID the same way. 177 | We then use Azure's Find Similar API to identify a 178 | potential match with our database of missing person Face 179 | IDs. 180 | If a match is found we will report to the local 181 | authorities and contact you. 182 |

    183 |
    184 |
  • 185 | 186 |
  • 187 | 192 |
    193 |

    194 | Once you have successfully reported a suspected missing 195 | person, 196 | we will cross-verify the report to check its validity. 197 | We then generate a unique Face ID for the missing person 198 | using Azure's Face API, 199 | followed by which, we use Azure's Find Similar API to 200 | identify a potential match with 201 | our database of missing person Face IDs. 202 | If a match is found we will report to the local 203 | authorities and contact the family/guardian. 204 |

    205 |
    206 |
  • 207 | 208 |
  • 209 | 213 |
    214 |

    215 | Firstly, it detects a face in the given image and 216 | generates 217 | a unique identifier string for the detected face in the 218 | image. 219 | Then to check if two images contain the same person, 220 | we use the Find Similar API, that searchs for 221 | similar-looking faces 222 | given a target Face ID and Face ID array. 223 | The API returns a JSON object containing the matched 224 | Face ID from the 225 | given array along with the confidence score. 226 |

    227 |
    228 |
  • 229 | 230 |
  • 231 | 236 | 237 |
    238 |

    239 | Thank you for your interest. Every help towards 240 | finding a loved one gone missing 241 | counts. You can help us in 242 | the following ways,
    243 | Please 244 | volunteer to spot suspected missing people 245 | and report them.
    246 | Connect us 247 | with NGOs working in this space.
    248 | Connect us with 249 | relevant state and central 250 | government departments.
    251 | Sponsor hosting 252 | fees.
    253 | Sponsor Azure API 254 | charges.
    255 | Contact us at 256 | facefindxyz@gmail.com for more details.
    257 |

    258 | 259 |
    260 |
  • 261 | 262 | 263 |
264 |
265 | 266 |
267 |
268 | 269 | 270 |
271 | 272 | 273 | 274 | 282 |
283 | 284 | 285 | 286 |
287 | 288 | 289 | {% endblock content%} -------------------------------------------------------------------------------- /landing/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /landing/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import IndexView 3 | 4 | urlpatterns = [ 5 | path('', IndexView.as_view(), name='index'), 6 | 7 | 8 | 9 | ] -------------------------------------------------------------------------------- /landing/views.py: -------------------------------------------------------------------------------- 1 | import imp 2 | from django.shortcuts import render 3 | 4 | # Create your views here. 5 | from django.views.generic import TemplateView 6 | 7 | 8 | class IndexView(TemplateView): 9 | template_name = "landing/index.html" 10 | 11 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'msengage.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /msengage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/msengage/__init__.py -------------------------------------------------------------------------------- /msengage/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for msengage 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/4.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', 'msengage.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /msengage/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for msengage project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.0. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.0/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import os 15 | import json 16 | 17 | with open('./config.json', 'r') as f: 18 | config = json.load(f) 19 | 20 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 21 | BASE_DIR = Path(__file__).resolve().parent.parent 22 | 23 | 24 | # Quick-start development settings - unsuitable for production 25 | # See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/ 26 | 27 | # SECURITY WARNING: keep the secret key used in production secret! 28 | SECRET_KEY = 'django-insecure-xgfz+t%xz6m=$sf6af@=^hm%+ew&#pscf$7(+x56f_u*&l(fz$' 29 | 30 | # SECURITY WARNING: don't run with debug turned on in production! 31 | DEBUG = True 32 | 33 | ALLOWED_HOSTS = ['*'] 34 | 35 | 36 | # Application definition 37 | 38 | INSTALLED_APPS = [ 39 | 'django.contrib.admin', 40 | 'django.contrib.auth', 41 | 'django.contrib.contenttypes', 42 | 'django.contrib.sessions', 43 | 'django.contrib.messages', 44 | 'django.contrib.staticfiles', 45 | 46 | 'landing', 47 | 'people', 48 | ] 49 | 50 | MIDDLEWARE = [ 51 | 'django.middleware.security.SecurityMiddleware', 52 | 'django.contrib.sessions.middleware.SessionMiddleware', 53 | 'django.middleware.common.CommonMiddleware', 54 | 'django.middleware.csrf.CsrfViewMiddleware', 55 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 56 | 'django.contrib.messages.middleware.MessageMiddleware', 57 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 58 | ] 59 | 60 | ROOT_URLCONF = 'msengage.urls' 61 | 62 | TEMPLATES = [ 63 | { 64 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 65 | 'DIRS': [os.path.join(BASE_DIR, 'templates')], 66 | 'APP_DIRS': True, 67 | 'OPTIONS': { 68 | 'context_processors': [ 69 | 'django.template.context_processors.debug', 70 | 'django.template.context_processors.request', 71 | 'django.contrib.auth.context_processors.auth', 72 | 'django.contrib.messages.context_processors.messages', 73 | ], 74 | }, 75 | }, 76 | ] 77 | 78 | WSGI_APPLICATION = 'msengage.wsgi.application' 79 | 80 | 81 | # Database 82 | # https://docs.djangoproject.com/en/4.0/ref/settings/#databases 83 | 84 | DATABASES = { 85 | 'default': { 86 | 'ENGINE': 'django.db.backends.sqlite3', 87 | 'NAME': BASE_DIR / 'msengage.sqlite3', 88 | } 89 | } 90 | 91 | 92 | # Password validation 93 | # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators 94 | 95 | AUTH_PASSWORD_VALIDATORS = [ 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 104 | }, 105 | { 106 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 107 | }, 108 | ] 109 | 110 | 111 | # Internationalization 112 | # https://docs.djangoproject.com/en/4.0/topics/i18n/ 113 | 114 | LANGUAGE_CODE = 'en-us' 115 | 116 | TIME_ZONE = 'Asia/Kolkata' 117 | 118 | USE_I18N = True 119 | 120 | # USE_TZ = True 121 | 122 | 123 | # Static files (CSS, JavaScript, Images) 124 | # https://docs.djangoproject.com/en/4.0/howto/static-files/ 125 | 126 | print ("Using local static and media files") 127 | STATIC_URL = '/static/' 128 | STATIC_ROOT = os.path.join(BASE_DIR, 'static') 129 | print ("Static Files should be at : ", STATIC_ROOT) 130 | 131 | MEDIA_URL = "/media/" 132 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 133 | print ("Media Files should be at : ", MEDIA_ROOT) 134 | 135 | 136 | # Default primary key field type 137 | # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field 138 | 139 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 140 | LOGIN_REDIRECT_URL = 'index' 141 | LOGOUT_REDIRECT_URL = 'index' 142 | 143 | 144 | # for sending emails 145 | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 146 | EMAIL_HOST = 'smtp.gmail.com' 147 | EMAIL_USE_TLS = True 148 | EMAIL_PORT = 587 149 | EMAIL_HOST_USER = config['EMAIL-ID'] 150 | EMAIL_HOST_PASSWORD = config['EMAIL-PASSWORD'] 151 | -------------------------------------------------------------------------------- /msengage/urls.py: -------------------------------------------------------------------------------- 1 | """msengage URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.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.contrib import admin 17 | from django.urls import path, include 18 | 19 | # used for defining static and image files 20 | from django.conf.urls.static import static 21 | from django.conf import settings 22 | 23 | 24 | urlpatterns = [ 25 | path('admin/', admin.site.urls), 26 | path('', include('landing.urls')), 27 | path('people/', include('people.urls')), 28 | path('accounts/', include('django.contrib.auth.urls')), 29 | 30 | 31 | 32 | ] 33 | 34 | 35 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 36 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 37 | 38 | -------------------------------------------------------------------------------- /msengage/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for msengage 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/4.0/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'msengage.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /people/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/people/__init__.py -------------------------------------------------------------------------------- /people/admin.py: -------------------------------------------------------------------------------- 1 | from email.errors import MissingHeaderBodySeparatorDefect 2 | from django.contrib import admin 3 | 4 | # Register your models here. 5 | from .models import MissingPerson, ReportedPerson 6 | 7 | admin.site.register(MissingPerson) 8 | 9 | admin.site.register(ReportedPerson) -------------------------------------------------------------------------------- /people/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PeopleConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'people' 7 | -------------------------------------------------------------------------------- /people/forms.py: -------------------------------------------------------------------------------- 1 | 2 | from django import forms 3 | 4 | from people.models import MissingPerson, ReportedPerson 5 | #Forms for the missing person db 6 | class MissingPersonCreateForm(forms.ModelForm): 7 | class Meta: 8 | model = MissingPerson 9 | # fields = "__all__" 10 | exclude = ['status', 'is_verified', 'face_id', 'found_location', 'found_time', 'is_contacted'] 11 | 12 | 13 | class MissingPersonUpdateForm(forms.ModelForm): 14 | class Meta: 15 | model = MissingPerson 16 | exclude = ['face_id', 'is_verified', 'found_location', 'found_time', 'is_contacted'] 17 | # fields = "__all__" 18 | 19 | 20 | class MissingPersonVerifyForm(forms.ModelForm): 21 | class Meta: 22 | model = MissingPerson 23 | # exclude = ['face_id'] 24 | fields = ['is_verified'] 25 | 26 | 27 | #Forms for the reported person db 28 | class ReportedPersonCreateForm(forms.ModelForm): 29 | class Meta: 30 | model = ReportedPerson 31 | # fields = "__all__" 32 | exclude = ['is_verified', 'face_id','is_matched_with_missing_person','matched_confindence', 'matched_face_id' ] 33 | 34 | 35 | class ReportedPersonUpdateForm(forms.ModelForm): 36 | class Meta: 37 | model = ReportedPerson 38 | exclude = ['face_id'] 39 | # fields = "__all__" 40 | 41 | 42 | class ReportedPersonVerifyForm(forms.ModelForm): 43 | class Meta: 44 | model = ReportedPerson 45 | # exclude = ['face_id'] 46 | fields = ['is_verified'] -------------------------------------------------------------------------------- /people/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-19 09:22 2 | 3 | from django.db import migrations, models 4 | import phonenumber_field.modelfields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='MisingPerson', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('first_name', models.CharField(max_length=200, unique=True, verbose_name='Given Name')), 20 | ('last_name', models.CharField(max_length=200, unique=True, verbose_name='Last Name')), 21 | ('age', models.CharField(blank=True, max_length=200, null=True, verbose_name='Age of Missing Person')), 22 | ('gender', models.CharField(choices=[('Male', 'Male'), ('Female', 'Female'), ('Other', 'Other')], max_length=200, unique=True, verbose_name='Gender of Missing Person')), 23 | ('last_seen', models.CharField(max_length=200, unique=True, verbose_name='Last Seen Location')), 24 | ('description', models.TextField(max_length=1000, verbose_name='Any Other Important Details')), 25 | ('photo', models.ImageField(blank=True, null=True, upload_to='missingpersons/', verbose_name='Upload Photo of Missing Person')), 26 | ('contact_person', models.CharField(max_length=200, unique=True, verbose_name='Contact Person')), 27 | ('contact_relationship', models.CharField(choices=[('Father', 'Father'), ('Mother', 'Mother'), ('Brother', 'Brother'), ('Sister', 'Sister'), ('Husband', 'Husband'), ('Wife', 'Wife'), ('Guardian', 'Guardian'), ('Relative', 'Relative'), ('Friend', 'Friend'), ('Other', 'Other')], max_length=200, unique=True, verbose_name='Relationship with Missing Person')), 28 | ('phone', phonenumber_field.modelfields.PhoneNumberField(blank=True, max_length=128, null=True, region=None, verbose_name='Contact Number')), 29 | ('status', models.CharField(choices=[('New', 'New'), ('Leads', 'Leads'), ('Found', 'Found'), ('Closed', 'Closed')], max_length=200, unique=True, verbose_name='Current Status')), 30 | ('created_date', models.DateTimeField(auto_now_add=True)), 31 | ('modified_date', models.DateTimeField(auto_now=True)), 32 | ], 33 | options={ 34 | 'ordering': ('first_name',), 35 | }, 36 | ), 37 | ] 38 | -------------------------------------------------------------------------------- /people/migrations/0002_rename_misingperson_missingperson_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-19 09:25 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameModel( 14 | old_name='MisingPerson', 15 | new_name='MissingPerson', 16 | ), 17 | migrations.AlterModelOptions( 18 | name='missingperson', 19 | options={'ordering': ('first_name',), 'verbose_name': 'Mising People', 'verbose_name_plural': 'Missing People'}, 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /people/migrations/0003_alter_missingperson_contact_person_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-19 10:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0002_rename_misingperson_missingperson_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='missingperson', 15 | name='contact_person', 16 | field=models.CharField(max_length=200, verbose_name='Contact Person'), 17 | ), 18 | migrations.AlterField( 19 | model_name='missingperson', 20 | name='contact_relationship', 21 | field=models.CharField(choices=[('Father', 'Father'), ('Mother', 'Mother'), ('Brother', 'Brother'), ('Sister', 'Sister'), ('Husband', 'Husband'), ('Wife', 'Wife'), ('Guardian', 'Guardian'), ('Relative', 'Relative'), ('Friend', 'Friend'), ('Other', 'Other')], max_length=200, verbose_name='Relationship with Missing Person'), 22 | ), 23 | migrations.AlterField( 24 | model_name='missingperson', 25 | name='first_name', 26 | field=models.CharField(max_length=200, verbose_name='Given Name'), 27 | ), 28 | migrations.AlterField( 29 | model_name='missingperson', 30 | name='gender', 31 | field=models.CharField(choices=[('Male', 'Male'), ('Female', 'Female'), ('Other', 'Other')], max_length=200, verbose_name='Gender of Missing Person'), 32 | ), 33 | migrations.AlterField( 34 | model_name='missingperson', 35 | name='last_name', 36 | field=models.CharField(max_length=200, verbose_name='Last Name'), 37 | ), 38 | migrations.AlterField( 39 | model_name='missingperson', 40 | name='last_seen', 41 | field=models.CharField(max_length=200, verbose_name='Last Seen Location'), 42 | ), 43 | migrations.AlterField( 44 | model_name='missingperson', 45 | name='status', 46 | field=models.CharField(choices=[('New', 'New'), ('Leads', 'Leads'), ('Found', 'Found'), ('Closed', 'Closed')], max_length=200, verbose_name='Current Status'), 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /people/migrations/0004_alter_missingperson_phone.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-19 12:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0003_alter_missingperson_contact_person_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='missingperson', 15 | name='phone', 16 | field=models.CharField(blank=True, max_length=10, null=True, verbose_name='Contact Number'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0004_missingperson_face_id_missingperson_is_verified.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-20 09:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0003_alter_missingperson_contact_person_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='missingperson', 15 | name='face_id', 16 | field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Face ID of Missing Person'), 17 | ), 18 | migrations.AddField( 19 | model_name='missingperson', 20 | name='is_verified', 21 | field=models.BooleanField(default=False, verbose_name='Person Background Check Done?'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /people/migrations/0005_reportedperson.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-20 09:47 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0004_missingperson_face_id_missingperson_is_verified'), 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='ReportedPerson', 15 | fields=[ 16 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 17 | ('last_seen', models.CharField(max_length=200, verbose_name='Last Seen Location')), 18 | ('description', models.TextField(max_length=1000, verbose_name='Any Other Important Details')), 19 | ('photo', models.ImageField(upload_to='reportedpersons/', verbose_name='Upload Photo of Reported Person')), 20 | ('is_verified', models.BooleanField(default=False, verbose_name='Is this valid reporting?')), 21 | ('face_id', models.CharField(blank=True, max_length=200, null=True, verbose_name='Face ID of Reported Person')), 22 | ('created_date', models.DateTimeField(auto_now_add=True)), 23 | ('modified_date', models.DateTimeField(auto_now=True)), 24 | ], 25 | options={ 26 | 'verbose_name': 'Reported People', 27 | 'verbose_name_plural': 'Reported People', 28 | 'ordering': ('created_date',), 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /people/migrations/0006_reportedperson_is_matched_with_missing_person_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-20 11:13 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0005_reportedperson'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='reportedperson', 15 | name='is_matched_with_missing_person', 16 | field=models.BooleanField(default=False, verbose_name='Has the match found?'), 17 | ), 18 | migrations.AddField( 19 | model_name='reportedperson', 20 | name='matched_confindence', 21 | field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Details of Match'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /people/migrations/0007_reportedperson_matched_face_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0 on 2022-05-20 12:11 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0006_reportedperson_is_matched_with_missing_person_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='reportedperson', 15 | name='matched_face_id', 16 | field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Face ID of Matched Person'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0008_merge_20220520_1911.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-20 13:41 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0004_alter_missingperson_phone'), 10 | ('people', '0007_reportedperson_matched_face_id'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /people/migrations/0009_alter_missingperson_age_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-24 09:49 2 | 3 | from django.db import migrations, models 4 | import phonenumber_field.modelfields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('people', '0008_merge_20220520_1911'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='missingperson', 16 | name='age', 17 | field=models.CharField(max_length=200, verbose_name='Age of Missing Person'), 18 | ), 19 | migrations.AlterField( 20 | model_name='missingperson', 21 | name='description', 22 | field=models.TextField(blank=True, max_length=1000, null=True, verbose_name='Any Other Important Details'), 23 | ), 24 | migrations.AlterField( 25 | model_name='missingperson', 26 | name='phone', 27 | field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, region=None, verbose_name='Contact Number'), 28 | ), 29 | migrations.AlterField( 30 | model_name='missingperson', 31 | name='photo', 32 | field=models.ImageField(upload_to='missingpersons/', verbose_name='Upload Photo of Missing Person'), 33 | ), 34 | migrations.AlterField( 35 | model_name='missingperson', 36 | name='status', 37 | field=models.CharField(choices=[('New', 'New'), ('Leads', 'Leads'), ('Found', 'Found'), ('Closed', 'Closed')], default='New', max_length=200, verbose_name='Current Status'), 38 | ), 39 | ] 40 | -------------------------------------------------------------------------------- /people/migrations/0010_alter_reportedperson_description_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-24 09:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0009_alter_missingperson_age_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='reportedperson', 15 | name='description', 16 | field=models.TextField(blank=True, max_length=1000, null=True, verbose_name='Any Other Important Details'), 17 | ), 18 | migrations.AlterField( 19 | model_name='reportedperson', 20 | name='last_seen', 21 | field=models.CharField(max_length=200, verbose_name='Found Location'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /people/migrations/0011_missingperson_contact_email.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-26 13:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0010_alter_reportedperson_description_and_more'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='missingperson', 15 | name='contact_email', 16 | field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='Contact Email ID'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0012_alter_missingperson_contact_email.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-26 13:30 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0011_missingperson_contact_email'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='missingperson', 15 | name='contact_email', 16 | field=models.EmailField(max_length=254, verbose_name='Contact Email ID'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0013_missingperson_found_location.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-26 13:32 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0012_alter_missingperson_contact_email'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='missingperson', 15 | name='found_location', 16 | field=models.CharField(blank=True, max_length=200, null=True, verbose_name='Found Location'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0014_rename_last_seen_reportedperson_reported_location.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-26 13:35 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0013_missingperson_found_location'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='reportedperson', 15 | old_name='last_seen', 16 | new_name='reported_location', 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0015_missingperson_found_time.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-26 13:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0014_rename_last_seen_reportedperson_reported_location'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='missingperson', 15 | name='found_time', 16 | field=models.DateTimeField(blank=True, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/0016_missingperson_is_contacted.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.0.4 on 2022-05-26 15:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('people', '0015_missingperson_found_time'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='missingperson', 15 | name='is_contacted', 16 | field=models.BooleanField(default=False, verbose_name='Contact person has been informed?'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /people/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/people/migrations/__init__.py -------------------------------------------------------------------------------- /people/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from phonenumber_field.modelfields import PhoneNumberField 4 | 5 | # missing person model 6 | class MissingPerson(models.Model): 7 | 8 | GENDER_CATEGORY_CHOICES = ( 9 | ("Male", "Male"), 10 | ("Female", "Female"), 11 | ("Other", "Other"), 12 | ) 13 | 14 | RELATIONSHIP_CATEGORY_CHOICES = ( 15 | ("Father", "Father"), 16 | ("Mother", "Mother"), 17 | ("Brother", "Brother"), 18 | ("Sister", "Sister"), 19 | ("Husband", "Husband"), 20 | ("Wife", "Wife"), 21 | ("Guardian", "Guardian"), 22 | ("Relative", "Relative"), 23 | ("Friend", "Friend"), 24 | ("Other", "Other"), 25 | 26 | ) 27 | 28 | CURRENT_STATUS_CHOICES = ( 29 | ("New", "New"), 30 | ("Leads", "Leads"), 31 | ("Found", "Found"), 32 | ("Closed", "Closed"), 33 | ) 34 | # basic info fields 35 | first_name = models.CharField(verbose_name="Given Name", max_length=200, blank=False, null=False) 36 | last_name = models.CharField(verbose_name="Last Name", max_length=200, blank=False, null=False) 37 | age = models.CharField(verbose_name="Age of Missing Person",max_length=200, blank=False, null=False, ) 38 | gender = models.CharField(verbose_name="Gender of Missing Person", choices=GENDER_CATEGORY_CHOICES, max_length=200, blank=False, null=False) 39 | last_seen = models.CharField(verbose_name="Last Seen Location", max_length=200, blank=False, null=False) 40 | description = models.TextField(verbose_name="Any Other Important Details",max_length=1000, blank=True, null=True) 41 | photo = models.ImageField(verbose_name="Upload Photo of Missing Person",upload_to="missingpersons/", blank=False, null=False ) 42 | 43 | # info of person to be contacted if missing person found 44 | contact_person = models.CharField(verbose_name="Contact Person", max_length=200, blank=False, null=False) 45 | contact_relationship = models.CharField(verbose_name="Relationship with Missing Person", choices=RELATIONSHIP_CATEGORY_CHOICES, max_length=200, blank=False, null=False) 46 | contact_email = models.EmailField(verbose_name="Contact Email ID",max_length=254, blank=False, null=False) 47 | phone = PhoneNumberField(verbose_name="Contact Number", null=False, blank=False, ) 48 | 49 | # currents status of case ie new/leads/found/closed 50 | status = models.CharField(verbose_name="Current Status", choices=CURRENT_STATUS_CHOICES, max_length=200, blank=False, null=False, default="New") 51 | 52 | # fields used in AI face recognition 53 | is_verified = models.BooleanField(verbose_name="Person Background Check Done?", default=False) 54 | face_id = models.CharField(verbose_name="Face ID of Missing Person",max_length=200, blank=True, null=True, ) 55 | 56 | # if found, location from where the volunteer reported 57 | found_location = models.CharField(verbose_name="Found Location", max_length=200, blank=True, null=True) 58 | found_time = models.DateTimeField( blank=True, null=True) 59 | is_contacted = models.BooleanField(verbose_name="Contact person has been informed?", default=False) 60 | 61 | created_date = models.DateTimeField(auto_now_add=True) 62 | modified_date = models.DateTimeField(auto_now=True) 63 | 64 | class Meta: 65 | verbose_name = 'Mising People' 66 | verbose_name_plural = 'Missing People' 67 | ordering = ('first_name', ) 68 | 69 | def __str__(self): 70 | return str(self.first_name) + " " + str(self.last_name) + " " + str(self.face_id) 71 | 72 | 73 | 74 | class ReportedPerson(models.Model): 75 | 76 | reported_location = models.CharField(verbose_name="Found Location", max_length=200, blank=False, null=False) 77 | description = models.TextField(verbose_name="Any Other Important Details",max_length=1000, blank=True, null=True) 78 | photo = models.ImageField(verbose_name="Upload Photo of Reported Person",upload_to="reportedpersons/", null=False, blank=False,) 79 | 80 | # fields used in AI face recognition 81 | is_verified = models.BooleanField(verbose_name="Is this valid reporting?", default=False) 82 | face_id = models.CharField(verbose_name="Face ID of Reported Person",max_length=200, blank=True, null=True, ) 83 | is_matched_with_missing_person = models.BooleanField(verbose_name="Has the match found?", default=False) 84 | matched_confindence = models.CharField(verbose_name="Details of Match", max_length=200, blank=True, null=True) 85 | matched_face_id = models.CharField(verbose_name="Face ID of Matched Person",max_length=200, blank=True, null=True, ) 86 | 87 | created_date = models.DateTimeField(auto_now_add=True) 88 | modified_date = models.DateTimeField(auto_now=True) 89 | 90 | class Meta: 91 | verbose_name = 'Reported People' 92 | verbose_name_plural = 'Reported People' 93 | ordering = ('created_date', ) 94 | 95 | def __str__(self): 96 | return str(self.created_date) 97 | -------------------------------------------------------------------------------- /people/templates/people/create_update_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 | 11 | {%if object%} 12 |

Update a Missing Person

13 | 14 | {%else %} 15 |

Register a Missing Person

16 |

Enter details of the missing person. Make sure to upload a clear picture of the persons face. 17 |

18 | {%endif%} 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | {% csrf_token %} 30 | 31 | 32 |
33 | {{ form.as_table }} 34 |
35 |
36 | 37 | 38 |
39 | 40 | 41 | 42 |
43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/delete_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 |

Are you sure you want to delete?

11 |

Once deleted data cannot be recovered. 12 |

13 |
14 | 15 |
16 | 17 | 18 |
19 | {% csrf_token %} 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | 28 | 29 | 30 |
31 |
32 | 33 | 34 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/found_person_details.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 |

Potential match found!

11 | 12 | 13 |
14 | 15 |
16 |
17 |
18 |
Registered Image 20 |

21 | 22 | Reported Image 23 |

24 |
25 |

{{found_person_details.0.first_name}} {{found_person_details.0.last_name}}

26 | Last seen at : {{found_person_details.0.last_seen}} 27 |

28 |

    29 |
  • Age : {{found_person_details.0.age}}
  • 30 |
  • Gender : {{found_person_details.0.gender}}
  • 31 |
  • Description : {{found_person_details.0.description}}
  • 32 |
  • Contact Person : {{found_person_details.0.contact_person}}
  • 33 |
  • Contact Email : {{found_person_details.0.contact_email}}
  • 34 |
  • Contact Phone : {{found_person_details.0.phone}}
  • 35 |
36 |

37 | 38 | 39 | {%if not object.is_contacted%} 40 |
41 | Confirm and Match 43 |
44 | {%endif%} 45 |
46 | 47 |
48 | 49 |
50 | 51 | 52 | 53 |
54 | 55 |
56 |
57 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/missing_person_form_success.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 |

Successfully Registered!

11 |

We will contact you for a background check soon.

12 |
13 |
14 |
15 | 16 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/missing_person_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 |

List of Missing People

11 |
12 | 13 |
14 | 15 | {% for object in missing_persons %} 16 |
17 |
18 |
20 |
21 |

{{ object.first_name }} {{ object.last_name }}

22 | Last seen at {{ object.last_seen}}, Status : {{object.status}} 23 |

{{ object.description }}

24 |
25 |
26 | Edit   28 | Delete   30 | 31 | {%if not object.is_verified%} 32 | Approve   34 | {%endif%} 35 | 36 |
37 |
38 |
39 |
40 | {% empty %} 41 | No entries available. 42 | 43 | {% endfor %} 44 | 45 | 46 | 47 |
48 | 49 |
50 |
51 | 52 | 53 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/missing_person_matched.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 |
6 |
7 | 8 |
9 |

Missing person has been matched!

10 |

{{missing_person_object.first_name}}'s {{missing_person_object.contact_relationship}}, 11 | {{missing_person_object.contact_person}} will be contacted and informed shortly!

12 |
13 | 14 | 15 |
16 |
17 | 18 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/reported_create_update_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 | 11 | {%if object%} 12 |

Update a Reported Person

13 | 14 | {%else %} 15 |

Report a Missing Person

16 |

Enter details of the person found. Make sure to upload a clear picture of the persons face. 17 |

18 | {%endif%} 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 |
28 | {% csrf_token %} 29 | 30 | 31 |
32 | {{ form.as_table }} 33 |
34 |
35 | 36 | 37 |
38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 |
46 | 47 |
48 |
49 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/reported_delete_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 |
6 | {% csrf_token %} 7 | 8 | 9 |
10 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/reported_person_form_success.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 |

Successfully Reported!

11 |

Thank you for reporting. 12 | We will inform the authorities if there is a match with 13 | a missing person in our database.

14 |
15 |
16 |
17 | 18 | {% endblock content %} -------------------------------------------------------------------------------- /people/templates/people/reported_person_list.html: -------------------------------------------------------------------------------- 1 | {% extends 'landing/base.html' %} 2 | {% load static %} 3 | 4 | {% block content %} 5 | 6 |
7 |
8 | 9 |
10 |

List of Reported People

11 |
12 | 13 |
14 | 15 | {% for object in reported_persons %} 16 |
17 |
18 |
20 |
21 |

Reported on {{ object.created_date.date}}

22 | {{ object.description }} 23 | {% if object.is_matched_with_missing_person %} 24 |

{{ object.matched_confindence }} Details 26 | Here!

27 | {% else %} 28 |

Nothing Found

29 | 30 | 31 | {% endif %} 32 |
33 |
34 |
35 | Edit   37 | Delete   39 | 40 | {%if not object.is_verified%} 41 | Approve   43 | {%endif%} 44 | 45 |
46 |
47 |
48 |
49 | {% empty %} 50 | No entries available. 51 | 52 | {% endfor %} 53 | 54 | 55 | 56 |
57 | 58 |
59 |
60 | 61 | 62 | {% endblock content %} -------------------------------------------------------------------------------- /people/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /people/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | 4 | from .views import * 5 | urlpatterns = [ 6 | # urls for missing person list views 7 | path('list-missing-person/', MissingPersonListView.as_view(), name='list_missing_person'), 8 | path('list-missing-person-to-be-verified/', MissingPersonToBeApprovedListView.as_view(), name='list_missing_person_to_be_verified'), 9 | path('list-missing-person-with-leads/', MissingPersonWithLeadsListView.as_view(), name='list_missing_person_with_leads'), 10 | path('list-missing-person-found/', MissingPersonFoundListView.as_view(), name='list_missing_person_found'), 11 | 12 | # urls for missing person create, update verify, delete 13 | path('create-missing-person/', MissingPersonCreateView.as_view(), name='create_missing_person'), 14 | path('update-missing-person/', MisssingPersonUpdateView.as_view(), name='update_missing_person'), 15 | path('delete-missing-person/', MisssingPersonDeleteView.as_view(), name='delete_missing_person'), 16 | path('verify-missing-person/', MisssingPersonVerifyView.as_view(), name='verify_missing_person'), 17 | 18 | # url to update status of missing person 19 | path('missing-person-match/', missing_person_update_status, name='missing_person_match'), 20 | 21 | # urls for reported person list views 22 | path('list-reported-person/', ReportedPersonListView.as_view(), name='list_reported_person'), 23 | path('list-reported-person-to-be-verified/', ReportedPersonToBeApprovedListView.as_view(), name='list_reported_person_to_be_verified'), 24 | path('list-reported-person-match-found/', ReportedPersonMatchedListView.as_view(), name='list_reported_person_match_found'), 25 | path('list-reported-person-match-not-found/', ReportedPersonNotMatchedListView.as_view(), name='list_reported_person_match_not_found'), 26 | 27 | # urls for reported person create, update verify, delete 28 | path('create-reported-person/', ReportedPersonCreateView.as_view(), name='create_reported_person'), 29 | path('update-reported-person/', ReportedPersonUpdateView.as_view(), name='update_reported_person'), 30 | path('delete-reported-person/', ReportedPersonDeleteView.as_view(), name='delete_reported_person'), 31 | path('verify-reported-person/', ReportedPersonVerifyView.as_view(), name='verify_reported_person'), 32 | 33 | # url for showing found/matched person 34 | path('show-found-person/', FoundPersonTemplateView.as_view(), name='show_found_person'), 35 | 36 | # urls for form success 37 | path('missing-person-form-success/', MissingPersonFormSuccessView.as_view(), name='missing_person_form_success'), 38 | path('reported-person-form-success/', ReportedPersonFormSuccessView.as_view(), name='reported_person_form_success'), 39 | 40 | 41 | 42 | 43 | 44 | ] -------------------------------------------------------------------------------- /people/views.py: -------------------------------------------------------------------------------- 1 | from pipes import Template 2 | from django.shortcuts import render 3 | 4 | from django.views.generic import ListView, DetailView, TemplateView 5 | from django.views.generic.edit import CreateView, DeleteView, UpdateView 6 | 7 | from django.urls import reverse_lazy 8 | 9 | from people.models import MissingPerson, ReportedPerson 10 | from .forms import * 11 | from azure.cognitiveservices.vision.face import FaceClient 12 | from msrest.authentication import CognitiveServicesCredentials 13 | from django.contrib.auth.mixins import LoginRequiredMixin 14 | 15 | import json 16 | 17 | from django.shortcuts import get_object_or_404 18 | from django.conf import settings 19 | from django.core.mail import send_mail 20 | 21 | # to get api credentials 22 | with open('./config.json', 'r') as f: 23 | config = json.load(f) 24 | 25 | 26 | ## MISSING PERSONS 27 | # view to list all missing people 28 | class MissingPersonListView(LoginRequiredMixin, ListView): 29 | login_url = reverse_lazy('index') 30 | logout_url = reverse_lazy('index') 31 | model = MissingPerson 32 | template_name = 'people/missing_person_list.html' 33 | context_object_name = "missing_persons" 34 | 35 | # to view list of all missing people who need to be approved/verified 36 | class MissingPersonToBeApprovedListView(LoginRequiredMixin, ListView): 37 | login_url = reverse_lazy('index') 38 | logout_url = reverse_lazy('index') 39 | template_name = 'people/missing_person_list.html' 40 | context_object_name = 'missing_persons' 41 | queryset = MissingPerson.objects.filter(is_verified=False) 42 | 43 | # to view list of all missing people with status as leads (possible match with a reported person) 44 | class MissingPersonWithLeadsListView(LoginRequiredMixin, ListView): 45 | login_url = reverse_lazy('index') 46 | logout_url = reverse_lazy('index') 47 | template_name = 'people/missing_person_list.html' 48 | context_object_name = 'missing_persons' 49 | queryset = MissingPerson.objects.filter(status='Leads') 50 | 51 | # to view list of all missing people who have been found 52 | class MissingPersonFoundListView(LoginRequiredMixin, ListView): 53 | login_url = reverse_lazy('index') 54 | logout_url = reverse_lazy('index') 55 | template_name = 'people/missing_person_list.html' 56 | context_object_name = 'missing_persons' 57 | queryset = MissingPerson.objects.filter(status='Found') 58 | 59 | # view to create a missing person 60 | class MissingPersonCreateView(CreateView): 61 | model = MissingPerson 62 | form_class = MissingPersonCreateForm 63 | template_name = 'people/create_update_form.html' 64 | success_url = reverse_lazy ('missing_person_form_success') 65 | 66 | # view to update a missing person 67 | class MisssingPersonUpdateView(LoginRequiredMixin, UpdateView): 68 | login_url = reverse_lazy('index') 69 | logout_url = reverse_lazy('index') 70 | model = MissingPerson 71 | form_class = MissingPersonUpdateForm 72 | template_name = 'people/create_update_form.html' 73 | success_url = reverse_lazy ('list_missing_person') 74 | 75 | # function to generate face_id using Azure Face API 76 | def generate_face_id(image_path): 77 | face_client = FaceClient(config['ENDPOINT'], CognitiveServicesCredentials(config['KEY'])) 78 | response_detected_face = face_client.face.detect_with_stream( 79 | image=open(image_path, 'rb'), 80 | detection_model='detection_03', 81 | recognition_model='recognition_04', 82 | ) 83 | return response_detected_face 84 | 85 | # function to find a match for the reported person from the list of missing people using Azure Face API 86 | def find_match(reported_face_id, missing_face_ids): 87 | face_client = FaceClient(config['ENDPOINT'], CognitiveServicesCredentials(config['KEY'])) 88 | matched_faces = face_client.face.find_similar( 89 | face_id=reported_face_id, 90 | face_ids=missing_face_ids 91 | ) 92 | return matched_faces 93 | 94 | # view to verify a missing person (if background check is done) 95 | class MisssingPersonVerifyView(LoginRequiredMixin, UpdateView): 96 | login_url = reverse_lazy('index') 97 | logout_url = reverse_lazy('index') 98 | model = MissingPerson 99 | form_class = MissingPersonVerifyForm 100 | template_name = 'people/create_update_form.html' 101 | success_url = reverse_lazy ('list_missing_person') 102 | 103 | def post(self, request, **kwargs): 104 | print ("Catching Update Function") 105 | 106 | form = self.form_class(request.POST) 107 | if form.is_valid(): 108 | if form.cleaned_data['is_verified']: 109 | self.object = self.get_object() 110 | print ("image URL is", self.object.photo.url) 111 | print ("image Path is", self.object.photo.path) 112 | 113 | print("face ID is",self.object.face_id) 114 | 115 | # if the person is verified and does not already have a face id, we generate one 116 | if not self.object.face_id: 117 | print ("Calling Face ID Generation") 118 | 119 | # generating face id 120 | response_detected_face=generate_face_id(self.object.photo.path) 121 | print ("Detected Face ID is",response_detected_face[0].face_id) 122 | 123 | # saving the generated face id to database 124 | self.object.face_id = response_detected_face[0].face_id 125 | self.object.save() 126 | return super().post(request, **kwargs) 127 | 128 | # view to delete a missing person 129 | class MisssingPersonDeleteView(LoginRequiredMixin, DeleteView): 130 | login_url = reverse_lazy('index') 131 | logout_url = reverse_lazy('index') 132 | model = MissingPerson 133 | template_name = 'people/delete_form.html' 134 | success_url = reverse_lazy ('list_missing_person') 135 | 136 | ## REPORTED PERSONS 137 | # view to list all reported people 138 | class ReportedPersonListView(LoginRequiredMixin, ListView): 139 | login_url = reverse_lazy('index') 140 | logout_url = reverse_lazy('index') 141 | model = ReportedPerson 142 | template_name = 'people/reported_person_list.html' 143 | context_object_name = "reported_persons" 144 | 145 | # to view list of all reported people who need to be approved/verified 146 | class ReportedPersonToBeApprovedListView(LoginRequiredMixin, ListView): 147 | login_url = reverse_lazy('index') 148 | logout_url = reverse_lazy('index') 149 | template_name = 'people/reported_person_list.html' 150 | context_object_name = 'reported_persons' 151 | queryset = ReportedPerson.objects.filter(is_verified=False) 152 | 153 | # to view list of all reported people who have been matched with a missing person 154 | class ReportedPersonMatchedListView(LoginRequiredMixin, ListView): 155 | login_url = reverse_lazy('index') 156 | logout_url = reverse_lazy('index') 157 | template_name = 'people/reported_person_list.html' 158 | context_object_name = 'reported_persons' 159 | queryset = ReportedPerson.objects.filter(is_matched_with_missing_person=True) 160 | 161 | 162 | # to view list of all reported people who have not yet been matched with a missing person 163 | class ReportedPersonNotMatchedListView(LoginRequiredMixin, ListView): 164 | login_url = reverse_lazy('index') 165 | logout_url = reverse_lazy('index') 166 | template_name = 'people/reported_person_list.html' 167 | context_object_name = 'reported_persons' 168 | queryset = ReportedPerson.objects.filter(is_matched_with_missing_person=False, is_verified=True) 169 | 170 | # view to create reported people 171 | class ReportedPersonCreateView(CreateView): 172 | model = ReportedPerson 173 | form_class = ReportedPersonCreateForm 174 | template_name = 'people/reported_create_update_form.html' 175 | success_url = reverse_lazy ('reported_person_form_success') 176 | 177 | # view to update reported people 178 | class ReportedPersonUpdateView(LoginRequiredMixin, UpdateView): 179 | login_url = reverse_lazy('index') 180 | logout_url = reverse_lazy('index') 181 | model = ReportedPerson 182 | form_class = ReportedPersonUpdateForm 183 | template_name = 'people/reported_create_update_form.html' 184 | success_url = reverse_lazy ('list_reported_person') 185 | 186 | # view to verify a reported person 187 | class ReportedPersonVerifyView(LoginRequiredMixin, UpdateView): 188 | login_url = reverse_lazy('index') 189 | logout_url = reverse_lazy('index') 190 | model = ReportedPerson 191 | form_class = ReportedPersonVerifyForm 192 | template_name = 'people/reported_create_update_form.html' 193 | success_url = reverse_lazy ('list_reported_person') 194 | 195 | def post(self, request, **kwargs): 196 | print ("Catching Update Function") 197 | 198 | form = self.form_class(request.POST) 199 | if form.is_valid(): 200 | if form.cleaned_data['is_verified']: 201 | self.object = self.get_object() 202 | print ("image URL is", self.object.photo.url) 203 | print ("image Path is", self.object.photo.path) 204 | 205 | print("face ID is",self.object.face_id) 206 | 207 | # to get a list of all face_ids of missing persons 208 | missing_persons_face_ids = list(MissingPerson.objects.filter(face_id__isnull=False).values_list('face_id', flat=True)) 209 | # missing_persons_names = list(MissingPerson.objects.all().values_list('first_name', flat=True)) 210 | # db = dict(zip(missing_persons_names, missing_persons_face_ids)) 211 | 212 | # if the person is verified and does not already have a face id, we generate one 213 | if not self.object.face_id: 214 | print ("Calling Face ID Generation") 215 | 216 | # generating face id 217 | response_detected_face=generate_face_id(self.object.photo.path) 218 | print ("Detected Face ID is",response_detected_face[0].face_id) 219 | 220 | # saving the generated face id to database 221 | self.object.face_id = response_detected_face[0].face_id 222 | self.object.save() 223 | 224 | # finding if there is a match 225 | matched_faces=find_match(self.object.face_id, missing_persons_face_ids) 226 | 227 | print(matched_faces) 228 | # if return list is not empty 229 | if len(matched_faces)!=0: 230 | # if the matched face returned exists in list of missing people 231 | if MissingPerson.objects.filter(face_id=matched_faces[0].face_id).exists(): 232 | print ("match found !") 233 | print (matched_faces[0].face_id, matched_faces[0].confidence) 234 | 235 | # getting the matched missing person 236 | found_person = MissingPerson.objects.get(face_id = matched_faces[0].face_id) 237 | found_person.status="Leads" 238 | found_person.found_location=self.object.reported_location 239 | found_person.found_time=self.object.created_date 240 | 241 | found_person.save() 242 | 243 | # Updating matched details to reported database 244 | self.object.matched_face_id = matched_faces[0].face_id 245 | self.object.is_matched_with_missing_person = True 246 | self.object.matched_confindence = "This could be " + found_person.first_name + " lost at " + found_person.last_seen + " reported by "+ found_person.contact_person + " with confidence rate of " + str(matched_faces[0].confidence*100) +"%." 247 | self.object.save() 248 | 249 | return super().post(request, **kwargs) 250 | 251 | # view to delete reported person 252 | class ReportedPersonDeleteView(LoginRequiredMixin, DeleteView): 253 | login_url = reverse_lazy('index') 254 | logout_url = reverse_lazy('index') 255 | model = ReportedPerson 256 | template_name = 'people/delete_form.html' 257 | success_url = reverse_lazy ('list_reported_person') 258 | 259 | # view to display matched/found person details 260 | class FoundPersonTemplateView(LoginRequiredMixin, TemplateView): 261 | login_url = reverse_lazy('index') 262 | logout_url = reverse_lazy('index') 263 | model = MissingPerson 264 | template_name = 'people/found_person_details.html' 265 | 266 | 267 | def get_context_data(self,**kwargs): 268 | context = super().get_context_data(**kwargs) 269 | context['reported_person_details'] = ReportedPerson.objects.filter(matched_face_id = self.kwargs['face_id'] ) 270 | 271 | context['found_person_details'] = MissingPerson.objects.filter(face_id = self.kwargs['face_id'] ) 272 | return context 273 | 274 | # view to display missing person has been successfully registered 275 | class MissingPersonFormSuccessView(TemplateView): 276 | template_name= 'people/missing_person_form_success.html' 277 | 278 | # view to display reported person has been successfully registered 279 | class ReportedPersonFormSuccessView(TemplateView): 280 | template_name= 'people/reported_person_form_success.html' 281 | 282 | # fuction to send contact person a mail 283 | def SendEmailToContact(object): 284 | subject = f'We have found {object.first_name}!' 285 | message = f'Hi {object.contact_person}, {object.first_name} {object.last_name} was reported to be found at {object.found_location} on {object.found_time}.' 286 | email_from = settings.EMAIL_HOST_USER 287 | recipient_list = [object.contact_email, ] 288 | send_mail( subject, message, email_from, recipient_list ) 289 | 290 | # function to set status as found and send email to contact person when "Confirm and match" button is clicked 291 | def missing_person_update_status(request, pk): 292 | object = get_object_or_404(MissingPerson, pk=pk) 293 | object.status = "Found" 294 | # contacting the relative/guardian 295 | SendEmailToContact(object) 296 | object.is_contacted = True 297 | object.save() 298 | print("Email sent!") 299 | context = {'missing_person_object': object} 300 | 301 | return render(request, "people/missing_person_matched.html", context) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/requirements.txt -------------------------------------------------------------------------------- /screenshots/details_of_match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/screenshots/details_of_match.png -------------------------------------------------------------------------------- /screenshots/diagrams/business_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/screenshots/diagrams/business_architecture.png -------------------------------------------------------------------------------- /screenshots/diagrams/lessons_learnt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/screenshots/diagrams/lessons_learnt.png -------------------------------------------------------------------------------- /screenshots/diagrams/tech_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/screenshots/diagrams/tech_architecture.png -------------------------------------------------------------------------------- /screenshots/email_received.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinycoder2/FaceFind/5166345fc8a12caa1b4fa2fddfb6db1e8503baf2/screenshots/email_received.png -------------------------------------------------------------------------------- /templates/registration/logged_out.html: -------------------------------------------------------------------------------- 1 | {% extends "landing/base.html" %} 2 | 3 | {% block content %} 4 | 5 | 6 |
7 |
8 | 9 |
10 | 11 |

Logged out!

12 | Click here to login again. 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 | {% endblock %} -------------------------------------------------------------------------------- /templates/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends "landing/base.html" %} 2 | 3 | {% block content %} 4 | 5 | 6 |
7 |
8 | 9 |
10 | {% if form.errors %} 11 |

Your username and password didn't match. Please try again.

12 | {% endif %} 13 | 14 | {% if next %} 15 | {% if user.is_authenticated %} 16 |

Your account doesn't have access to this page. To proceed, 17 | please login with an account that has access.

18 | {% else %} 19 |

Please login to see this page.

20 | {% endif %} 21 | {% endif %} 22 | 23 |
24 | 25 |
26 | 27 | 28 | 29 |
30 |
31 | {% csrf_token %} 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
42 |
43 | 44 | 45 |

46 | {# Assumes you setup the password_reset view in your URLconf #} 47 | 48 |
49 | 50 |
51 |
52 | 53 |
54 | 55 | 56 | 57 |
58 |
59 | 60 | 61 | {% endblock %} -------------------------------------------------------------------------------- /templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends "landing/base.html" %} 2 | {% block content %} 3 | 4 |
5 |
6 | 7 |
8 | 9 |

The password has been changed!

10 |

log in again?

11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 | 21 | {% endblock %} -------------------------------------------------------------------------------- /templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "landing/base.html" %} 2 | 3 | 4 | {% block content %} 5 | 6 | 7 |
8 |
9 | 10 |
11 | {% if validlink %} 12 |

Please enter (and confirm) your new password.

13 |
14 | 15 |
16 | 17 | 18 | 19 |
20 |
21 | {% csrf_token %} 22 | 23 | 24 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
{{ form.new_password1.errors }} 25 | 26 | {{ form.new_password1 }}
{{ form.new_password2.errors }} 31 | 32 | {{ form.new_password2 }}
40 |
41 |
42 | 43 |
44 |
45 | {% else %} 46 |

Password reset failed

47 |

The password reset link was invalid, possibly because it has already been used. Please request a new 48 | password reset. 49 |

50 | {% endif %} 51 |
52 | 53 | 54 | 55 |
56 |
57 | 58 | 59 | 60 | 61 | {% endblock %} -------------------------------------------------------------------------------- /templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "landing/base.html" %} 2 | 3 | {% block content %} 4 | 5 | 6 |
7 |
8 | 9 |
10 | 11 | 12 |

We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check 13 | your spam 14 | folder.

15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | {% endblock %} -------------------------------------------------------------------------------- /templates/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | Someone asked for password reset for email {{ email }}. Follow the link below: 2 | {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

-------------------------------------------------------------------------------- /templates/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends "landing/base.html" %} 2 | 3 | {% block content %} 4 | 5 |
6 |
7 | 8 |
9 | 10 | 11 |

Enter email for sending password reset link :

12 | 13 |
14 | 15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | {% csrf_token %} 24 | {% if form.email.errors %} 25 | {{ form.email.errors }} 26 | {% endif %} 27 |

{{ form.email }}

28 | 29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | {% endblock %} --------------------------------------------------------------------------------