├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── assets
├── Automato Logo - squiggle.svg
├── automato.svg
└── header.png
├── automato-front.bat
├── automato-serve.bat
├── automato-service.bat
├── automato.bat
├── back
├── app.py
├── automato_winservice.py
├── config.py
├── consumer.py
├── csv_load.py
├── json-load.py
├── migrations
│ ├── README
│ ├── alembic.ini
│ ├── env.py
│ ├── script.py.mako
│ └── versions
│ │ ├── 033d3c3ffe79_.py
│ │ ├── b04011c66f30_.py
│ │ └── bf3c038f77b8_.py
├── model.py
├── requirements.txt
├── routes
│ ├── __init__.py
│ ├── city.py
│ ├── contacts.py
│ ├── groups.py
│ ├── job.py
│ ├── scraper.py
│ ├── settings.py
│ ├── tag.py
│ └── templates.py
├── run.py
├── scraper
│ └── __init__.py
├── task.db
└── tasks.py
└── front
├── .editorconfig
├── README.md
├── assets
├── README.md
├── b-style.scss
└── main.css
├── components
├── EditContact.vue
├── EditTemplate.vue
├── Logo.vue
├── NewContact.vue
├── NewGroup.vue
├── NewImport.vue
├── NewJob.vue
├── NewScraper.vue
├── NewTemplate.vue
└── README.md
├── jsconfig.json
├── layouts
├── README.md
└── default.vue
├── middleware
└── README.md
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
├── README.md
├── contacts.vue
├── groups.vue
├── index.vue
├── jobs.vue
├── master.vue
├── master
│ ├── city.vue
│ ├── index.vue
│ └── tag.vue
├── scrapers.vue
├── settings.vue
└── templates.vue
├── plugins
├── README.md
├── buefy.js
└── feather.js
├── static
├── README.md
├── favicon.ico
├── main.scss
└── uploads
│ └── templates
│ ├── 000000.jpg
│ ├── 11111.png
│ ├── 700rapid (1).jpg
│ ├── IMG_4467_1024x1024.webp
│ ├── KENDRICK_NAVY_F_1024x1024.webp
│ ├── image-indiamart-resized.jpg
│ ├── itmanan_png_white.png
│ ├── mememe.jpg
│ └── sign-mememem.jpg
└── store
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | uENV/
89 | venv/
90 | ENV/
91 | env.bak/
92 | venv.bak/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
107 | # VSCode
108 | .vscode
109 |
110 |
111 | back/user-data-wp/
112 |
113 | static/uploads/
114 |
115 | # dependencies
116 | node_modules
117 |
118 | # logs
119 | npm-debug.log
120 |
121 | # Nuxt build
122 | .nuxt
123 |
124 | # Nuxt generate
125 | dist
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at padamsethia5@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Padam Sethia
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
Web Application to automate sending Whatsapp Messages , SMS & Email campaigns( WIP ).
12 |
13 | ## Features
14 |
15 | * Scrape business data from Google Maps , IndiaMart ( more coming soon )
16 | * Mange your contacts , tag them to send specialized campaigns.
17 | * Manage and edit contact/business .
18 | * Import/ Export Data ( WIP ) out of Automato
19 | * Setup templates for campaigns
20 | * Send Whatsapp and SMS campaigns without any extra cost
21 |
22 | ## Installation & Setup
23 |
24 |
25 | *NOTE:* You'll need to use python3.
26 |
27 | To install requirements :
28 |
29 | > Clone or download https://github.com/padamsethia/automato
30 |
31 | > Run pip install -r requirements.txt in automato/back
32 |
33 | > Run npm install in automato/front
34 |
35 | > npm run dev to run the frontend at automato/front
36 |
37 | > python run.py to run the backend at automato/back
38 |
39 | > python huey-consumer.py tasks.huey to run the consumer at automato/back
40 |
41 | Automato requires the following softwares to work , please follow the setup from the offical docs.
42 |
43 | * [Selenium](https://www.seleniumhq.org/)
44 | * [MySQL](https://www.mysql.com/downloads/) as backend database
45 |
46 |
47 | ## Issues and Requests
48 | For any issue or requests , kindly use Github Issues.
49 |
50 | ## Projects Used
51 | * [Bulma CSS](http://getskeleton.com)
52 | * Flask
53 | * Buefy
54 | * Vue.js
55 | * SQLAlchemy
56 | * Albemic
57 |
58 | ## License
59 | This project is licensed under the [MIT License](./LICENSE).
60 |
--------------------------------------------------------------------------------
/assets/Automato Logo - squiggle.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/assets/automato.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/assets/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/assets/header.png
--------------------------------------------------------------------------------
/automato-front.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cmd /k "cd /d %CD%\front & npm run start &"
--------------------------------------------------------------------------------
/automato-serve.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cmd /k "cd /d %CD%\back\ENV\Scripts\ & activate & cd /d %CD%\back & python run.py"
--------------------------------------------------------------------------------
/automato-service.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | cmd /k "cd /d %CD%\back\ENV\Scripts\ & activate & cd /d %CD%\back & python .\ENV\Lib\site-packages\huey\bin\huey_consumer.py tasks.huey"
--------------------------------------------------------------------------------
/automato.bat:
--------------------------------------------------------------------------------
1 | @start "" /B %SystemRoot%\System32\cmd.exe /C "%CD%\automato-front.bat"
2 | @start "" /B %SystemRoot%\System32\cmd.exe /C "%CD%\automato-serve.bat"
3 | @start "" /B %SystemRoot%\System32\cmd.exe /C "%CD%\automato-service.bat"
4 |
--------------------------------------------------------------------------------
/back/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | from flask import Flask, request , jsonify
4 | from flask_sqlalchemy import SQLAlchemy
5 | from flask_migrate import Migrate
6 | from config import Config
7 | from werkzeug.debug import DebuggedApplication
8 | from flask_marshmallow import Marshmallow
9 | from flask_cors import CORS
10 | import json
11 | from sqlalchemy.exc import IntegrityError
12 | app = Flask(__name__)
13 | app.config.from_object(Config)
14 | CORS(app)
15 |
16 | UPLOAD_FOLDER = os.path.abspath(app.config['UPLOAD_FOLDER'])
17 |
18 |
19 | db = SQLAlchemy(app)
20 | migrate = Migrate(app ,db)
21 | ma = Marshmallow(app)
22 |
23 | from routes import templates , contacts , city , tag , scraper , groups , job , settings
24 |
--------------------------------------------------------------------------------
/back/automato_winservice.py:
--------------------------------------------------------------------------------
1 | import win32serviceutil
2 | import win32service
3 | import win32event
4 | import servicemanager
5 | import logging
6 | import time
7 | from app import app, db
8 | from gevent.pywsgi import WSGIServer
9 | import socket
10 | import webbrowser
11 |
12 | logging.basicConfig(
13 | filename='.\\automato-service.log',
14 | level=logging.DEBUG,
15 | format='[automato-service] %(levelname)-7.7s %(message)s'
16 | )
17 |
18 | global http_server
19 |
20 | class AutomatoSvc(win32serviceutil.ServiceFramework):
21 | _svc_name_ = "Automato-Service"
22 | _svc_display_name_ = "Automato Service"
23 |
24 | def __init__(self,args):
25 | win32serviceutil.ServiceFramework.__init__(self,args)
26 | self.stop_event = win32event.CreateEvent(None,0,0,None)
27 | socket.setdefaulttimeout(60)
28 | self.stop_requested = False
29 |
30 | def SvcStop(self):
31 | self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
32 | win32event.SetEvent(self.stop_event)
33 | logging.info('Stopping service ...')
34 | http_server.stop()
35 | self.stop_requested = True
36 |
37 | def SvcDoRun(self):
38 | servicemanager.LogMsg(
39 | servicemanager.EVENTLOG_INFORMATION_TYPE,
40 | servicemanager.PYS_SERVICE_STARTED,
41 | (self._svc_name_,'')
42 | )
43 | self.main()
44 |
45 | def main(self):
46 | logging.info('Automato Windows Service Started')
47 | # Simulate a main loop
48 | db.create_all()
49 | logging.info('Starting server')
50 | global http_server
51 | http_server = WSGIServer(('0.0.0.0' , 80) , app)
52 | http_server.serve_forever()
53 | webbrowser.open_new_tab('localhost')
54 |
55 | if __name__ == '__main__':
56 | win32serviceutil.HandleCommandLine(AutomatoSvc)
--------------------------------------------------------------------------------
/back/config.py:
--------------------------------------------------------------------------------
1 | # Config for automato
2 | # WTF config
3 | class Config(object):
4 |
5 | WTF_CSRF_ENABLED = True
6 | SECRET_KEY = 'the_very_secure_secret_security_key_that_no_will_ever_guess'
7 |
8 | # MySQL Config
9 |
10 | SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:alpine@127.0.0.1/reautomato'
11 | SQLALCHEMY_TRACK_MODIFICATIONS = False
12 | RABBITMQ_HOST = "localhost"
13 |
14 | WEBDRIVER_PATH = r"C:\Users\padam\Downloads\chromedriver_win32\chromedriver.exe"
15 | TESTING = False
16 |
17 | UPLOAD_FOLDER = './../front/static/uploads'
18 |
19 |
--------------------------------------------------------------------------------
/back/consumer.py:
--------------------------------------------------------------------------------
1 | from google import huey
2 |
3 | if __name__ == "__main__":
4 |
5 | print('Running')
--------------------------------------------------------------------------------
/back/csv_load.py:
--------------------------------------------------------------------------------
1 | import csv , os
2 | from app import db
3 | from model import Contact , City , Tag
4 | from sqlalchemy.exc import IntegrityError
5 |
6 | file_json = r""
7 |
8 | path = os.path.abspath(file_json)
9 | with open(path) as f:
10 | data = csv.reader(f)
11 |
12 | tags = ['suit', 'dupatta', 'home', 'skirt', 'pant', 'palazzo', 'tops', 'saree', 'blouse' , 'fabric' , 'dress material' , 'bag' , 'kurti' , 'bedsheet' , 'kurta']
13 |
14 | def check(string, sub_str):
15 | if (string.find(sub_str) != -1):
16 | return sub_str
17 |
18 |
19 | for item in data:
20 |
21 | name = item['contacts_name'] + '-' + item['contacts_company']
22 | contact = str(item['contact_ph_country']) + str(item['contacts_mobile1'])
23 | city = item['contact_city']
24 | state = item['contact_state']
25 | country = item['country_name']
26 | enquiry = str(item['contact_last_product']).lower()
27 | tag = [y for y in (check(enquiry, x) for x in tags) if y is not None]
28 | tag.append('indiamart')
29 |
30 | if city == "":
31 | if state == "":
32 | city = country
33 | else:
34 | city = state
35 |
36 |
37 | # Checks for empty values
38 |
39 | try:
40 | city_obj = City.query.filter_by(name=str(city)).first()
41 |
42 | if city_obj:
43 | pass
44 | else:
45 | city_obj = City(str(city), str(state), str(country))
46 | db.session.add(city_obj)
47 |
48 |
49 | con_obj = Contact(name , contact , "" , "" , "" , city_obj)
50 | db.session.add(con_obj)
51 |
52 | for item in tag:
53 | tag_obj = Tag.query.filter_by(name=item).first()
54 | if tag_obj:
55 | pass
56 | else:
57 | tag_obj = Tag(str(item))
58 | db.session.add(tag_obj)
59 |
60 | con_obj.tag_contact.append(tag_obj)
61 |
62 | db.session.commit()
63 | print('Added Data ---- ' + name , city , contact)
64 | except IntegrityError:
65 | db.session.rollback()
66 | print('Duplicate ----- '+ name , city , contact)
67 | pass
68 | except Exception as e:
69 | db.session.rollback()
70 | print('Oh No ---------' , name, city , contact )
71 | pass
72 |
73 |
74 | import requests
75 | def check_api():
76 | data = json.loads(requests.get("http://api.geonames.org/postalCodeSearchJSON?placename=mumbai&username=padam&maxrows=100"))['postalCodes']
77 | area = [ x['placeName'] for x in data]
78 | print(area)
79 |
80 | if __name__ == "__main__":
81 | check_api
82 |
--------------------------------------------------------------------------------
/back/json-load.py:
--------------------------------------------------------------------------------
1 | import json , os
2 | from app import db
3 | from model import Contact , City , Tag
4 | from sqlalchemy.exc import IntegrityError
5 |
6 | file_json = r"C:\Users\Padam\Desktop\inmart.txt"
7 |
8 | path = os.path.abspath(file_json)
9 | with open(path) as f:
10 | data = json.load(f)['result']
11 |
12 | tags = ['suit', 'dupatta', 'home', 'skirt', 'pant', 'palazzo', 'tops', 'saree', 'blouse' , 'fabric' , 'dress material' , 'bag' , 'kurti' , 'bedsheet' , 'kurta']
13 |
14 | def check(string, sub_str):
15 | if (string.find(sub_str) != -1):
16 | return sub_str
17 |
18 |
19 | for item in data:
20 | if item['contacts_company'] is None:
21 | item['contacts_company'] = ""
22 | name = item['contacts_name'] + '-' + item['contacts_company']
23 | contact = str(item['contact_ph_country']) + str(item['contacts_mobile1'])
24 | city = item['contact_city']
25 | state = item['contact_state']
26 | country = item['country_name']
27 | enquiry = str(item['contact_last_product']).lower()
28 | tag = [y for y in (check(enquiry, x) for x in tags) if y is not None]
29 | tag.append('indiamart')
30 |
31 | if city == "":
32 | if state == "":
33 | city = country
34 | else:
35 | city = state
36 |
37 |
38 | # Checks for empty values
39 |
40 | try:
41 | city_obj = City.query.filter_by(name=str(city)).first()
42 |
43 | if city_obj:
44 | pass
45 | else:
46 | city_obj = City(str(city), str(state), str(country))
47 | db.session.add(city_obj)
48 |
49 |
50 | con_obj = Contact(name , contact , "" , "" , "" , city_obj)
51 | db.session.add(con_obj)
52 |
53 | for item in tag:
54 | tag_obj = Tag.query.filter_by(name=item).first()
55 | if tag_obj:
56 | pass
57 | else:
58 | tag_obj = Tag(str(item))
59 | db.session.add(tag_obj)
60 |
61 | con_obj.tag_contact.append(tag_obj)
62 |
63 | db.session.commit()
64 | print('Added Data ---- ' + name , city , contact)
65 | except IntegrityError:
66 | db.session.rollback()
67 | print('Duplicate ----- '+ name , city , contact)
68 | pass
69 | except Exception as e:
70 | db.session.rollback()
71 | print('Oh No ---------' , name, city , contact )
72 | pass
73 |
74 |
75 | import requests
76 | def check_api():
77 | data = json.loads(requests.get("http://api.geonames.org/postalCodeSearchJSON?placename=mumbai&username=padam&maxrows=100"))['postalCodes']
78 | area = [ x['placeName'] for x in data]
79 | print(area)
80 |
81 | if __name__ == "__main__":
82 | check_api
83 |
--------------------------------------------------------------------------------
/back/migrations/README:
--------------------------------------------------------------------------------
1 | Generic single-database configuration.
--------------------------------------------------------------------------------
/back/migrations/alembic.ini:
--------------------------------------------------------------------------------
1 | # A generic, single database configuration.
2 |
3 | [alembic]
4 | # template used to generate migration files
5 | # file_template = %%(rev)s_%%(slug)s
6 |
7 | # set to 'true' to run the environment during
8 | # the 'revision' command, regardless of autogenerate
9 | # revision_environment = false
10 |
11 |
12 | # Logging configuration
13 | [loggers]
14 | keys = root,sqlalchemy,alembic
15 |
16 | [handlers]
17 | keys = console
18 |
19 | [formatters]
20 | keys = generic
21 |
22 | [logger_root]
23 | level = WARN
24 | handlers = console
25 | qualname =
26 |
27 | [logger_sqlalchemy]
28 | level = WARN
29 | handlers =
30 | qualname = sqlalchemy.engine
31 |
32 | [logger_alembic]
33 | level = INFO
34 | handlers =
35 | qualname = alembic
36 |
37 | [handler_console]
38 | class = StreamHandler
39 | args = (sys.stderr,)
40 | level = NOTSET
41 | formatter = generic
42 |
43 | [formatter_generic]
44 | format = %(levelname)-5.5s [%(name)s] %(message)s
45 | datefmt = %H:%M:%S
46 |
--------------------------------------------------------------------------------
/back/migrations/env.py:
--------------------------------------------------------------------------------
1 | from __future__ import with_statement
2 |
3 | import logging
4 | from logging.config import fileConfig
5 |
6 | from sqlalchemy import engine_from_config
7 | from sqlalchemy import pool
8 |
9 | from alembic import context
10 |
11 | # this is the Alembic Config object, which provides
12 | # access to the values within the .ini file in use.
13 | config = context.config
14 |
15 | # Interpret the config file for Python logging.
16 | # This line sets up loggers basically.
17 | fileConfig(config.config_file_name)
18 | logger = logging.getLogger('alembic.env')
19 |
20 | # add your model's MetaData object here
21 | # for 'autogenerate' support
22 | # from myapp import mymodel
23 | # target_metadata = mymodel.Base.metadata
24 | from flask import current_app
25 | config.set_main_option(
26 | 'sqlalchemy.url', current_app.config.get(
27 | 'SQLALCHEMY_DATABASE_URI').replace('%', '%%'))
28 | target_metadata = current_app.extensions['migrate'].db.metadata
29 |
30 | # other values from the config, defined by the needs of env.py,
31 | # can be acquired:
32 | # my_important_option = config.get_main_option("my_important_option")
33 | # ... etc.
34 |
35 |
36 | def run_migrations_offline():
37 | """Run migrations in 'offline' mode.
38 |
39 | This configures the context with just a URL
40 | and not an Engine, though an Engine is acceptable
41 | here as well. By skipping the Engine creation
42 | we don't even need a DBAPI to be available.
43 |
44 | Calls to context.execute() here emit the given string to the
45 | script output.
46 |
47 | """
48 | url = config.get_main_option("sqlalchemy.url")
49 | context.configure(
50 | url=url, target_metadata=target_metadata, literal_binds=True
51 | )
52 |
53 | with context.begin_transaction():
54 | context.run_migrations()
55 |
56 |
57 | def run_migrations_online():
58 | """Run migrations in 'online' mode.
59 |
60 | In this scenario we need to create an Engine
61 | and associate a connection with the context.
62 |
63 | """
64 |
65 | # this callback is used to prevent an auto-migration from being generated
66 | # when there are no changes to the schema
67 | # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
68 | def process_revision_directives(context, revision, directives):
69 | if getattr(config.cmd_opts, 'autogenerate', False):
70 | script = directives[0]
71 | if script.upgrade_ops.is_empty():
72 | directives[:] = []
73 | logger.info('No changes in schema detected.')
74 |
75 | connectable = engine_from_config(
76 | config.get_section(config.config_ini_section),
77 | prefix='sqlalchemy.',
78 | poolclass=pool.NullPool,
79 | )
80 |
81 | with connectable.connect() as connection:
82 | context.configure(
83 | connection=connection,
84 | target_metadata=target_metadata,
85 | process_revision_directives=process_revision_directives,
86 | **current_app.extensions['migrate'].configure_args
87 | )
88 |
89 | with context.begin_transaction():
90 | context.run_migrations()
91 |
92 |
93 | if context.is_offline_mode():
94 | run_migrations_offline()
95 | else:
96 | run_migrations_online()
97 |
--------------------------------------------------------------------------------
/back/migrations/script.py.mako:
--------------------------------------------------------------------------------
1 | """${message}
2 |
3 | Revision ID: ${up_revision}
4 | Revises: ${down_revision | comma,n}
5 | Create Date: ${create_date}
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | ${imports if imports else ""}
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = ${repr(up_revision)}
14 | down_revision = ${repr(down_revision)}
15 | branch_labels = ${repr(branch_labels)}
16 | depends_on = ${repr(depends_on)}
17 |
18 |
19 | def upgrade():
20 | ${upgrades if upgrades else "pass"}
21 |
22 |
23 | def downgrade():
24 | ${downgrades if downgrades else "pass"}
25 |
--------------------------------------------------------------------------------
/back/migrations/versions/033d3c3ffe79_.py:
--------------------------------------------------------------------------------
1 | """empty message
2 |
3 | Revision ID: 033d3c3ffe79
4 | Revises: b04011c66f30
5 | Create Date: 2020-01-07 18:28:41.146110
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = '033d3c3ffe79'
14 | down_revision = 'b04011c66f30'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('job', sa.Column('meta', sa.String(length=250), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('job', 'meta')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/back/migrations/versions/b04011c66f30_.py:
--------------------------------------------------------------------------------
1 | """empty message
2 |
3 | Revision ID: b04011c66f30
4 | Revises:
5 | Create Date: 2020-01-06 11:39:46.970064
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 | from sqlalchemy.dialects import mysql
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'b04011c66f30'
14 | down_revision = None
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.create_table('club_job',
22 | sa.Column('id', sa.Integer(), nullable=False),
23 | sa.Column('club_id', sa.Integer(), nullable=True),
24 | sa.Column('job_id', sa.Integer(), nullable=True),
25 | sa.ForeignKeyConstraint(['club_id'], ['club.id'], ondelete='SET NULL'),
26 | sa.ForeignKeyConstraint(['job_id'], ['job.id'], ondelete='SET NULL'),
27 | sa.PrimaryKeyConstraint('id')
28 | )
29 | op.create_table('template_job',
30 | sa.Column('id', sa.Integer(), nullable=False),
31 | sa.Column('template_id', sa.Integer(), nullable=True),
32 | sa.Column('job_id', sa.Integer(), nullable=True),
33 | sa.ForeignKeyConstraint(['job_id'], ['job.id'], ondelete='SET NULL'),
34 | sa.ForeignKeyConstraint(['template_id'], ['template.id'], ondelete='SET NULL'),
35 | sa.PrimaryKeyConstraint('id')
36 | )
37 | op.drop_column('job', 'provider')
38 | # ### end Alembic commands ###
39 |
40 |
41 | def downgrade():
42 | # ### commands auto generated by Alembic - please adjust! ###
43 | op.add_column('job', sa.Column('provider', mysql.VARCHAR(length=50), nullable=True))
44 | op.drop_table('template_job')
45 | op.drop_table('club_job')
46 | # ### end Alembic commands ###
47 |
--------------------------------------------------------------------------------
/back/migrations/versions/bf3c038f77b8_.py:
--------------------------------------------------------------------------------
1 | """empty message
2 |
3 | Revision ID: bf3c038f77b8
4 | Revises: 033d3c3ffe79
5 | Create Date: 2020-01-13 17:08:16.904072
6 |
7 | """
8 | from alembic import op
9 | import sqlalchemy as sa
10 |
11 |
12 | # revision identifiers, used by Alembic.
13 | revision = 'bf3c038f77b8'
14 | down_revision = '033d3c3ffe79'
15 | branch_labels = None
16 | depends_on = None
17 |
18 |
19 | def upgrade():
20 | # ### commands auto generated by Alembic - please adjust! ###
21 | op.add_column('scrape', sa.Column('meta', sa.String(length=250), nullable=True))
22 | # ### end Alembic commands ###
23 |
24 |
25 | def downgrade():
26 | # ### commands auto generated by Alembic - please adjust! ###
27 | op.drop_column('scrape', 'meta')
28 | # ### end Alembic commands ###
29 |
--------------------------------------------------------------------------------
/back/model.py:
--------------------------------------------------------------------------------
1 | from app import db
2 | from app import ma
3 | from datetime import datetime
4 |
5 | from marshmallow_sqlalchemy import field_for
6 | from marshmallow import fields
7 |
8 |
9 |
10 |
11 | # Contact Template
12 | class Contact(db.Model):
13 | id = db.Column(db.Integer, primary_key=True)
14 | name = db.Column(db.String(250), nullable=False)
15 | contact_one = db.Column(db.String(50) , unique=True , default= None, nullable = False)
16 | contact_two = db.Column(db.String(50) , default="")
17 | address = db.Column(db.String(500) , default="")
18 | email = db.Column(db.String(200) , default="")
19 | city = db.relationship(
20 | 'City', cascade="all,delete", secondary='city_contact', backref='city_contact', lazy='joined')
21 |
22 | def __init__(self, name, contact_one, contact_two, address, email, city):
23 | self.name = name
24 | self.contact_one = contact_one
25 | self.contact_two = contact_two
26 | self.address = address
27 | self.email = email
28 | self.city.append(city)
29 |
30 |
31 |
32 | db.Table('city_contact',
33 | db.Column('id', db.Integer, primary_key=True),
34 | db.Column('city_id', db.Integer, db.ForeignKey(
35 | 'city.id', ondelete='SET NULL')),
36 | db.Column('contact_id', db.Integer, db.ForeignKey(
37 | 'contact.id', ondelete='SET NULL'))
38 | )
39 |
40 |
41 |
42 |
43 | class City(db.Model):
44 | id = db.Column(db.Integer, primary_key=True)
45 | name = db.Column(db.String(100) )
46 | state = db.Column(db.String(100) , default = "")
47 | country = db.Column(db.String(100) , default = "")
48 |
49 | def __init__(self, name, state, country):
50 | self.name = name
51 | self.state = state
52 | self.country = country
53 |
54 | class CitySchema(ma.ModelSchema):
55 | id = field_for(City, 'id', dump_only=True)
56 | name = field_for(City, 'name', dump_only=True)
57 | state = field_for(City, 'state', dump_only=True)
58 | country = field_for(City, 'country', dump_only=True)
59 |
60 | class meta:
61 | model = City
62 |
63 |
64 | db.Table('tag_contact',
65 | db.Column('id', db.Integer, primary_key=True),
66 | db.Column('tag_id', db.Integer, db.ForeignKey(
67 | 'tag.id', ondelete='SET NULL')),
68 | db.Column('contact_id', db.Integer, db.ForeignKey(
69 | 'contact.id', ondelete='SET NULL'))
70 | )
71 |
72 |
73 | class Tag(db.Model):
74 | id = db.Column(db.Integer, primary_key=True)
75 | name = db.Column(db.String(100))
76 | contact = db.relationship(
77 | 'Contact', cascade="all,delete", secondary='tag_contact', backref='tag_contact', lazy='joined')
78 | def __init__(self, name):
79 | self.name = name
80 |
81 | class TagSchema(ma.ModelSchema):
82 | id = field_for(Tag, 'id', dump_only=True)
83 | name = field_for(Tag, 'name', dump_only=True)
84 |
85 | class meta:
86 | model = Tag
87 |
88 | class Template(db.Model):
89 | id = db.Column(db.Integer, primary_key=True)
90 | name = db.Column(db.String(100) , unique = True)
91 | path = db.Column(db.String(200) , default = "")
92 | message = db.Column(db.String(500) , default= "")
93 | filetype = db.Column(db.String(100))
94 |
95 | def __init__(self, name, message, filetype):
96 | self.name = name
97 | self.message = message
98 | self.filetype = filetype
99 |
100 | class Club(db.Model):
101 | id = db.Column(db.Integer, primary_key=True)
102 | name = db.Column(db.String(100))
103 | contact = db.relationship(
104 | 'Contact', cascade="all,delete", secondary='club_contact', backref='club_contact', lazy='joined')
105 | tag = db.relationship(
106 | 'Tag', cascade="all,delete", secondary='club_tag', backref='club_tag', lazy='joined')
107 | city = db.relationship(
108 | 'City', cascade="all,delete", secondary='club_city', backref='club_city', lazy='joined')
109 |
110 | def __init__(self, name):
111 | self.name = name
112 |
113 | def get_contacts(self):
114 | return len(self.contact)
115 |
116 | db.Table('club_contact',
117 | db.Column('id', db.Integer, primary_key=True),
118 | db.Column('club_id', db.Integer, db.ForeignKey(
119 | 'club.id', ondelete='SET NULL')),
120 | db.Column('contact_id', db.Integer, db.ForeignKey(
121 | 'contact.id', ondelete='SET NULL'))
122 | )
123 | db.Table('club_tag',
124 | db.Column('id', db.Integer, primary_key=True),
125 | db.Column('club_id', db.Integer, db.ForeignKey(
126 | 'club.id', ondelete='SET NULL')),
127 | db.Column('tag_id', db.Integer, db.ForeignKey(
128 | 'tag.id', ondelete='SET NULL'))
129 | )
130 | db.Table('club_city',
131 | db.Column('id', db.Integer, primary_key=True),
132 | db.Column('club_id', db.Integer, db.ForeignKey(
133 | 'club.id', ondelete='SET NULL')),
134 | db.Column('city_id', db.Integer, db.ForeignKey(
135 | 'city.id', ondelete='SET NULL'))
136 | )
137 |
138 | class TemplateSchema(ma.ModelSchema):
139 | id = field_for(Template, 'id', dump_only=True)
140 | name = field_for(Template, 'name', dump_only=True)
141 | path = field_for(Template, 'path', dump_only=True)
142 | message = field_for(Template, 'message', dump_only=True)
143 | filetype = field_for(Template, 'filetype', dump_only=True)
144 |
145 | class meta:
146 | model = Template
147 |
148 | class Scrape(db.Model):
149 | id = db.Column(db.Integer, primary_key=True)
150 | city = db.relationship(
151 | 'City', cascade="all,delete", secondary='city_scrape', backref='city_scrape', lazy='joined')
152 | status = db.Column(db.String(10) , default="start")
153 | timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
154 | tag = db.relationship(
155 | 'Tag', cascade="all,delete", secondary='tag_scrape', backref='tag_scrape', lazy='joined')
156 | meta= db.Column(db.String(250) , default="")
157 | def __init__(self, city):
158 | self.city.append(city)
159 |
160 |
161 | db.Table('tag_scrape',
162 | db.Column('id', db.Integer, primary_key=True),
163 | db.Column('tag_id', db.Integer, db.ForeignKey(
164 | 'tag.id', ondelete='SET NULL')),
165 | db.Column('scrape_id', db.Integer, db.ForeignKey(
166 | 'scrape.id', ondelete='SET NULL'))
167 | )
168 | db.Table('city_scrape',
169 | db.Column('id', db.Integer, primary_key=True),
170 | db.Column('city_id', db.Integer, db.ForeignKey(
171 | 'city.id', ondelete='SET NULL')),
172 | db.Column('scrape_id', db.Integer, db.ForeignKey(
173 | 'scrape.id', ondelete='SET NULL'))
174 | )
175 |
176 | class ScrapeSchema(ma.ModelSchema):
177 | id = field_for(Scrape, 'id', dump_only=True)
178 | status = field_for(Scrape, 'status', dump_only=True)
179 | timestamp = field_for(Scrape, 'timestamp', dump_only=True)
180 | city = ma.Nested(CitySchema , many = True)
181 | tag = ma.Nested(TagSchema, many=True)
182 |
183 |
184 | class ContactSchema(ma.ModelSchema):
185 | id = field_for(Contact, 'id', dump_only=True)
186 | name = field_for(Contact, 'name', dump_only=True)
187 | contact_one = field_for(Contact, 'contact_one', dump_only=True)
188 | contact_two = field_for(Contact, 'contact_two', dump_only=True)
189 | address = field_for(Contact, 'address', dump_only=True)
190 | email = field_for(Contact, 'email', dump_only=True)
191 | city = ma.Nested(CitySchema , many = True)
192 | tag_contact = ma.Nested(TagSchema , many = True)
193 | class meta:
194 | model = Contact
195 |
196 | class ContactBaseSchema(ma.ModelSchema):
197 | id = field_for(Contact, 'id', dump_only=True)
198 | name = field_for(Contact, 'name', dump_only=True)
199 | contact_one = field_for(Contact, 'contact_one', dump_only=True)
200 | contact_two = field_for(Contact, 'contact_two', dump_only=True)
201 | address = field_for(Contact, 'address', dump_only=True)
202 | email = field_for(Contact, 'email', dump_only=True)
203 | city = ma.Nested(CitySchema , many = True)
204 | class meta:
205 | model = Contact
206 |
207 |
208 | db.Table('club_job',
209 | db.Column('id', db.Integer, primary_key=True),
210 | db.Column('club_id', db.Integer, db.ForeignKey(
211 | 'club.id', ondelete='SET NULL')),
212 | db.Column('job_id', db.Integer, db.ForeignKey(
213 | 'job.id', ondelete='SET NULL'))
214 | )
215 | db.Table('template_job',
216 | db.Column('id', db.Integer, primary_key=True),
217 | db.Column('template_id', db.Integer, db.ForeignKey(
218 | 'template.id', ondelete='SET NULL')),
219 | db.Column('job_id', db.Integer, db.ForeignKey(
220 | 'job.id', ondelete='SET NULL'))
221 | )
222 |
223 |
224 | class ClubSchema(ma.ModelSchema):
225 | id = field_for(Club, 'id', dump_only=True)
226 | name = field_for(Club, 'name', dump_only=True)
227 | tag = ma.Nested(TagSchema , many= True)
228 | city = ma.Nested(CitySchema , many= True)
229 | total = fields.Method('get_contacts_len')
230 |
231 | def get_contacts_len(self, obj):
232 | # base_city = Contact.query.join(City, Contact.city)
233 | # base_tag = Contact.query.join(Tag, Contact.tag_contact)
234 |
235 | # base = base_city.union(base_tag)
236 | if (len(obj.tag) == 0 and len(obj.city) != 0):
237 | total = len([t for x in obj.city for t in x.city_contact])
238 |
239 | elif (len(obj.city) == 0 and len(obj.tag) != 0):
240 | total = len([c for x in obj.tag for c in x.contact])
241 |
242 | elif (len(obj.city) != 0 and len(obj.tag) != 0):
243 |
244 | total_city = [t for x in obj.city for t in x.city_contact]
245 | total_tag = [c for x in obj.tag for c in x.contact]
246 | total = len( set(total_city) & set(total_tag))
247 | else:
248 | total = 0
249 | return { 'contact' : len(obj.contact) , 'tag': total }
250 |
251 | class meta:
252 | model = Club
253 |
254 | class ClubContactSchema(ma.ModelSchema):
255 | id = field_for(Club, 'id', dump_only=True)
256 | total_contact = fields.Method('get_contacts')
257 |
258 | def get_contacts(self, obj):
259 | # base_city = Contact.query.join(City, Contact.city)
260 | # base_tag = Contact.query.join(Tag, Contact.tag_contact)
261 |
262 | # base = base_city.union(base_tag)
263 | if (len(obj.tag) == 0 and len(obj.city) != 0):
264 | total = [t for x in obj.city for t in x.city_contact]
265 |
266 | elif (len(obj.city) == 0 and len(obj.tag) != 0):
267 | total = [c for x in obj.tag for c in x.contact]
268 |
269 | elif (len(obj.city) != 0 and len(obj.tag) != 0):
270 |
271 | total_city = [t for x in obj.city for t in x.city_contact]
272 | total_tag = [c for x in obj.tag for c in x.contact]
273 | total = set(total_city) & set(total_tag)
274 | else:
275 | total = 0
276 |
277 | schema = ContactBaseSchema(many=True)
278 |
279 | return schema.dump(total)
280 |
281 |
282 | class meta:
283 | model = Club
284 |
285 |
286 | class Job(db.Model):
287 | id = db.Column(db.Integer, primary_key=True)
288 | club = db.relationship(
289 | 'Club', cascade="all,delete", secondary='club_job', backref='club_job', lazy='joined')
290 | status = db.Column(db.String(10) , default="start")
291 | timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
292 | template = db.relationship(
293 | 'Template', cascade="all,delete", secondary='template_job', backref='template_job', lazy='joined')
294 | meta = db.Column( db.String(250) , default = None)
295 |
296 |
297 | class JobSchema(ma.ModelSchema):
298 | id = field_for(Scrape, 'id', dump_only=True)
299 | status = field_for(Job, 'status', dump_only=True)
300 | timestamp = field_for(Job, 'timestamp', dump_only=True)
301 | meta = field_for(Job, 'meta', dump_only=True)
302 | club = ma.Nested(ClubSchema , many = True)
303 | template = ma.Nested(TemplateSchema, many=True)
304 |
--------------------------------------------------------------------------------
/back/requirements.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/back/requirements.txt
--------------------------------------------------------------------------------
/back/routes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/back/routes/__init__.py
--------------------------------------------------------------------------------
/back/routes/city.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json , db , ma
2 |
3 | from model import City, CitySchema
4 |
5 | @app.route('/get/city', methods=['GET'])
6 | def get_city():
7 | if request.method == 'GET':
8 |
9 | data_schema = CitySchema(many=True)
10 | data = City.query.all()
11 | json_data = data_schema.dump(data)
12 | return jsonify(json_data)
13 |
14 |
15 | @app.route('/add/city', methods=['POST'])
16 | def add_city():
17 | if request.method == 'POST':
18 | payload = request.json
19 | print(payload)
20 | if len(payload['name']) != 0:
21 |
22 | check_data = City.query.filter_by(name=payload['name'].lower().strip())
23 | if check_data.first():
24 | return jsonify({'message': 'Data - '+check_data.first().name+' already exists.'})
25 | else:
26 | try:
27 | if 'state' not in payload.keys():
28 | payload['state'] = ""
29 | if 'country' not in payload.keys():
30 | payload['country'] = ""
31 |
32 | new_data = City(
33 | payload['name'].lower().strip() , payload['state'].lower().strip() , payload['country'].lower().strip())
34 |
35 | db.session.add(new_data)
36 | db.session.commit()
37 | json_data = { 'id' : new_data.id , 'name' : new_data.name}
38 | return jsonify({'success': 'Data Added', 'data' : json_data})
39 |
40 | except Exception as e:
41 | print(str(e))
42 | db.session.rollback()
43 | db.session.close()
44 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
45 | else:
46 | return jsonify({'message': 'Empty Data.'})
47 |
48 | else:
49 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
50 |
51 |
52 | @app.route('/edit/city', methods=['POST'])
53 | def edit_city():
54 | if request.method == 'POST':
55 |
56 | payload = request.json
57 | if payload['name'] is not None:
58 |
59 | check_data = City.query.filter_by(
60 | name=payload['name'].lower().strip()).first()
61 | if check_data and check_data.name != payload['name'].lower().strip():
62 | return jsonify({'message': 'Data - '+check_data.name+' already exists.'})
63 | else:
64 | try:
65 | new_data = City.query.filter_by(
66 | id=payload['id']).first()
67 | new_data.name = payload['name'].lower().strip()
68 | new_data.state = payload['state'].lower().strip()
69 | new_data.country = payload['country'].lower().strip()
70 | db.session.commit()
71 | return jsonify({'success': 'Data Updated'})
72 |
73 | except Exception as e:
74 | print(str(e))
75 |
76 | db.session.rollback()
77 | db.session.close()
78 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
79 | else:
80 | return jsonify({'message': 'Empty Data.'})
81 |
82 | else:
83 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
84 |
85 |
86 | @app.route('/delete/city', methods=['POST'])
87 | def delete_city():
88 | if request.method == 'POST':
89 | payload = request.json
90 | check_data = City.query.filter_by(id=payload['id'])
91 | if check_data.first():
92 | if len(check_data.first().city_contact) is int(0):
93 | try:
94 | check_data.delete()
95 | db.session.commit()
96 | return jsonify({'success': 'Data deleted'})
97 | except Exception as e:
98 | db.session.rollback()
99 | db.session.close()
100 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
101 | else:
102 | return jsonify({'message': 'Cannot delete data. Being used by other master.'})
103 |
104 | else:
105 | return jsonify({'message': 'Data does not exist.'})
106 |
107 | else:
108 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
109 |
--------------------------------------------------------------------------------
/back/routes/contacts.py:
--------------------------------------------------------------------------------
1 | import os
2 | from app import app, jsonify, request, json, UPLOAD_FOLDER
3 | from app import db
4 | from app import ma
5 |
6 | from sqlalchemy.exc import IntegrityError
7 | import csv
8 |
9 |
10 | from model import Contact, ContactSchema, ContactBaseSchema, City, Tag
11 |
12 |
13 | @app.route('/get/contacts', methods=['POST'])
14 | def get_contacts():
15 | payload = request.json
16 | schema = ContactBaseSchema(many=True)
17 | sort_order = payload['sort_order']
18 | sort_by = payload['sort_by']
19 | page = payload['page']
20 |
21 | data = Contact.query
22 | if sort_by == 'name' and sort_order == "desc":
23 | chain_data = data.order_by(Contact.name.desc())
24 | if sort_by == 'name' and sort_order == "asc":
25 | chain_data = data.order_by(Contact.name.asc())
26 |
27 | # if sort_by == 'city' and sort_order == "desc":
28 | # chain_data = data. .join(model.ClassificationItem).order_by(Contact.city[0].name.desc())
29 |
30 | # if sort_by == 'city' and sort_order == "asc":
31 | # chain_data = data.order_by(Contact.city[0].name.asc())
32 |
33 | if sort_by == 'id' and sort_order == "asc":
34 | chain_data = data.order_by(Contact.id.asc())
35 |
36 | if sort_by == 'id' and sort_order == "desc":
37 | chain_data = data.order_by(Contact.id.desc())
38 |
39 | if sort_by == 'contact_one' and sort_order == "asc":
40 | chain_data = data.order_by(Contact.contact_one.asc())
41 |
42 | if sort_by == 'contact_one' and sort_order == "desc":
43 | chain_data = data.order_by(Contact.contact_one.desc())
44 |
45 | data = chain_data.paginate(page, 20, False).items
46 | json_data = schema.dump(data)
47 | return jsonify(json_data)
48 |
49 |
50 | @app.route('/get/contacts/', methods=['POST'])
51 | def get_contacts_by_id(id):
52 | schema = ContactSchema()
53 | data = Contact.query.filter_by(id=int(id)).first()
54 | json_data = schema.dump(data)
55 | return jsonify(json_data)
56 |
57 |
58 | @app.route('/add/contacts', methods=['POST'])
59 | def add_contact():
60 | payload = request.json
61 | try:
62 |
63 | city = City.query.filter_by(id=int(payload['city'])).first()
64 | new_data = Contact(payload['name'], payload['contact_one'],
65 | payload['contact_two'], payload['address'], payload['email'], city)
66 |
67 | if (len(payload['tags']) is not 0):
68 |
69 | for item in payload['tags']:
70 | data = Tag.query.filter_by(
71 | id=item['id']).first()
72 | new_data.tag_contact.append(data)
73 |
74 | db.session.add(new_data)
75 | db.session.commit()
76 | return jsonify({'success': "Data added"})
77 |
78 | except Exception as e:
79 | # return Exception for duplicate data
80 | print(str(e))
81 | return jsonify({'message': "Something unexpected happened"})
82 |
83 |
84 | @app.route('/edit/contacts/', methods=['POST'])
85 | def edit_contact(id):
86 | payload = request.json
87 | try:
88 | new_data = Contact.query.filter_by(id=int(id)).first()
89 | new_data.tag_contact = []
90 |
91 | city = City.query.filter_by(id=int(payload['city'])).first()
92 | new_data.name = payload['name']
93 | new_data.contact_one = payload['contact_one']
94 | new_data.contact_two = payload['contact_two']
95 | new_data.address = payload['address']
96 | new_data.email = payload['email']
97 |
98 | if (len(payload['tags']) is not 0):
99 |
100 | for item in payload['tags']:
101 | data = Tag.query.filter_by(
102 | id=item['id']).first()
103 | new_data.tag_contact.append(data)
104 |
105 | db.session.commit()
106 | return jsonify({'success': "Data added"})
107 |
108 | except Exception as e:
109 | # return Exception for duplicate data
110 | print(str(e))
111 | return jsonify({'message': "Something unexpected happened"})
112 |
113 |
114 | @app.route('/delete/contacts/', methods=['POST'])
115 | def delete_contact(id):
116 |
117 | try:
118 | new_data = Contact.query.filter_by(id=int(id))
119 | if new_data.first():
120 | new_data.delete()
121 | db.session.commit()
122 | return jsonify({'success': "Data Deleted"})
123 |
124 | else:
125 | return jsonify({'message': "Data doesn't exists"})
126 | except Exception as e:
127 | # return Exception for duplicate data
128 | print(str(e))
129 |
130 |
131 | @app.route('/upload/contacts', methods=['POST'])
132 | def upload_contact():
133 | file = request.files['file']
134 | tags = json.loads(request.form['data'])
135 | try:
136 | if file:
137 | filename = file.filename
138 | foldertemp = os.path.join(
139 | UPLOAD_FOLDER, 'contacts')
140 |
141 | if os.path.exists(foldertemp):
142 | filetemp = os.path.join(
143 | foldertemp, filename)
144 | file.save(filetemp)
145 | else:
146 |
147 | os.makedirs(foldertemp)
148 |
149 | filetemp = os.path.join(
150 | foldertemp, filename)
151 | file.save(filetemp)
152 |
153 | with open(filetemp, mode="r") as csv_file:
154 |
155 | csv_data = list(csv.reader(csv_file, delimiter=","))
156 |
157 | for item in csv_data:
158 | name = item[0] + '-' + item[1]
159 | contact_one = str(item[6])
160 | contact_two = str(item[7])
161 | address = str(item[2])
162 | city = ''.join(i for i in item[3] if not i.isdigit()).lower().strip()
163 |
164 | state = item[4]
165 | country = item[5]
166 | email = item[8]
167 | tag = tags
168 | city = city.split('-')[0].split('(')[0].split(',')[0].title()
169 |
170 |
171 |
172 | if len(contact_one) == 10:
173 | contact_one = '91' + contact_one
174 |
175 | if city == "":
176 | if state == "":
177 | city = country.title()
178 | else:
179 | city = state.title()
180 |
181 | if item[0] is not "":
182 |
183 | try:
184 | city_obj = City.query.filter_by(name=str(city)).first()
185 |
186 | if city_obj:
187 | pass
188 | else:
189 |
190 | city_obj = City(str(city), str(state), str(country))
191 | db.session.add(city_obj)
192 |
193 | con_obj = Contact(name , contact_one , contact_two , address , email , city_obj)
194 | db.session.add(con_obj)
195 |
196 | for item in tag:
197 | tag_obj = Tag.query.filter_by(
198 | name=item['name']).first()
199 | if tag_obj:
200 | pass
201 | else:
202 | tag_obj = Tag(str(item['name']))
203 | db.session.add(tag_obj)
204 |
205 | con_obj.tag_contact.append(tag_obj)
206 | db.session.commit()
207 | except IntegrityError as e:
208 | db.session.rollback()
209 |
210 | try:
211 | con_obj = Contact.query.filter_by(contact_one=contact_one).first()
212 | con_obj.city = []
213 | db.session.commit()
214 | con_obj.city.append(city_obj)
215 | con_obj.name = name
216 | con_obj.address = address
217 | con_obj.email = email
218 | for item in tag:
219 | tag_obj = Tag.query.filter_by(
220 | name=item['name']).first()
221 | if tag_obj:
222 | pass
223 | else:
224 | tag_obj = Tag(str(item['name']))
225 | db.session.add(tag_obj)
226 |
227 | con_obj.tag_contact.append(tag_obj)
228 | db.session.commit()
229 |
230 | except Exception as f:
231 | db.session.rollback()
232 |
233 | except Exception as e:
234 | db.session.rollback()
235 |
236 |
237 |
238 | return jsonify({'success': 'Data Added'})
239 |
240 | except Exception as e:
241 | # return Exception for duplicate data
242 | print(str(e))
243 | return jsonify({'message': 'Returns error'})
244 |
--------------------------------------------------------------------------------
/back/routes/groups.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json , db , ma
2 |
3 | from model import Club, ClubSchema , Contact , Tag , City
4 |
5 |
6 | @app.route('/get/club', methods=['GET'])
7 | def get_club():
8 | if request.method == 'GET':
9 |
10 | data_schema = ClubSchema(many=True)
11 | data = Club.query.all()
12 | print(data)
13 | json_data = data_schema.dump(data)
14 | return jsonify(json_data)
15 |
16 |
17 | @app.route('/add/club', methods=['POST'])
18 | def add_club():
19 | if request.method == 'POST':
20 | payload = request.json
21 | print(payload)
22 | if len(payload['name']) != 0:
23 |
24 | check_data = Club.query.filter_by(name=payload['name'].lower().strip())
25 | if check_data.first():
26 | return jsonify({'message': 'Data - '+check_data.first().name+' already exists.'})
27 | else:
28 | try:
29 | new_data = Club(
30 | payload['name'].lower().strip())
31 | if (len(payload['contacts']) != 0):
32 | for item in payload['contacts']:
33 | print(item)
34 | data = Contact.query.filter_by(id=int(item['id'])).first()
35 | new_data.contact.append(data)
36 |
37 | db.session.add(new_data)
38 | db.session.commit()
39 | return jsonify({'success': 'Data Added'})
40 |
41 | else:
42 | tag = payload['tags']
43 | city = payload['city']
44 | for item in tag:
45 | print(item)
46 | data = Tag.query.filter_by(id=int(item['id'])).first()
47 | new_data.tag.append(data)
48 |
49 | for item in city:
50 | data = City.query.filter_by(id=int(item['id'])).first()
51 | new_data.city.append(data)
52 |
53 |
54 | db.session.commit()
55 | return jsonify({'success': 'Data Added'})
56 |
57 | except Exception as e:
58 | print(str(e))
59 | db.session.rollback()
60 | db.session.close()
61 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
62 | else:
63 | return jsonify({'message': 'Empty Data.'})
64 |
65 | else:
66 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
67 |
68 |
69 | @app.route('/edit/club', methods=['POST'])
70 | def edit_club():
71 | if request.method == 'POST':
72 |
73 | payload = request.json
74 | if payload['name'] is not None:
75 |
76 | check_data = Club.query.filter_by(
77 | name=payload['name'].lower().strip()).first()
78 | if check_data and check_data.name != payload['name'].lower().strip():
79 | return jsonify({'message': 'Data - '+check_data.name+' already exists.'})
80 | else:
81 | try:
82 | new_data = Club.query.filter_by(
83 | id=payload['id']).first()
84 | new_data.name = payload['name'].lower().strip()
85 | new_data.state = payload['state'].lower().strip()
86 | new_data.country = payload['country'].lower().strip()
87 | db.session.commit()
88 | return jsonify({'success': 'Data Updated'})
89 |
90 | except Exception as e:
91 | print(str(e))
92 |
93 | db.session.rollback()
94 | db.session.close()
95 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
96 | else:
97 | return jsonify({'message': 'Empty Data.'})
98 |
99 | else:
100 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
101 |
102 |
103 | @app.route('/delete/club/', methods=['POST'])
104 | def delete_club(id):
105 | if request.method == 'POST':
106 | payload = request.json
107 | check_data = Club.query.filter_by(id=int(id))
108 | if check_data.first():
109 | try:
110 | check_data.delete()
111 | db.session.commit()
112 | return jsonify({'success': 'Data deleted'})
113 | except Exception as e:
114 | db.session.rollback()
115 | db.session.close()
116 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
117 | else:
118 | return jsonify({'message': 'Data does not exist.'})
119 |
120 | else:
121 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
122 |
--------------------------------------------------------------------------------
/back/routes/job.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json , db , ma
2 |
3 | from model import Job, JobSchema , Club , Template , ClubContactSchema
4 |
5 | import tasks
6 | @app.route('/get/job', methods=['GET'])
7 | def get_job():
8 | if request.method == 'GET':
9 |
10 | data_schema = JobSchema(many=True)
11 | data = Job.query.all()
12 | json_data = data_schema.dump(data)
13 |
14 | return jsonify(json_data)
15 |
16 |
17 |
18 | @app.route('/run/job', methods=['POST'])
19 | def run_job():
20 | payload = request.json
21 | print(payload)
22 | job = Job.query.filter_by(id=int(payload['curr_id'])).first()
23 | meta = json.loads(job.meta)
24 | meta['timer'] = payload['timer']
25 | job.meta = json.dumps(meta)
26 | db.session.commit()
27 | try:
28 | task = tasks.whatsapp(job.id)
29 | print("heheheheheheheeh" + task.args)
30 | job.status = "running"
31 | db.session.commit()
32 | return jsonify({'success': 'Job started'})
33 | except Exception as e:
34 | return jsonify({'message': 'Unable to run job. Check logs'})
35 |
36 |
37 | @app.route('/run/job/sms', methods=['POST'])
38 | def run_job_sms():
39 | payload = request.json
40 | job = Job.query.filter_by(id=int(payload['curr_id'])).first()
41 | try:
42 | task = tasks.messages(job.id)
43 | return jsonify({'success': 'Job started'})
44 | except Exception as e:
45 | return jsonify({'message': 'Unable to run job. Check logs'})
46 |
47 |
48 | @app.route('/add/job', methods=['POST'])
49 | def add_job():
50 | if request.method == 'POST':
51 | payload = request.json
52 | print(payload)
53 | if payload:
54 | try:
55 | if (len(payload['club']) is not 0):
56 | template = Template.query.filter_by(id = int(payload['template'])).first()
57 | new_data = Job()
58 | new_data.template.append(template)
59 | meta = {'type': payload['type']}
60 | new_data.meta = json.dumps(meta)
61 | for item in payload['club']:
62 | print(item)
63 | data = Club.query.filter_by(
64 | id=item['id']).first()
65 | new_data.club.append(data)
66 |
67 | db.session.add(new_data)
68 | db.session.commit()
69 | return jsonify({'success': 'Data Added'})
70 |
71 | except Exception as e:
72 | print(str(e))
73 | db.session.rollback()
74 | db.session.close()
75 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
76 | else:
77 | return jsonify({'message': 'Empty Data.'})
78 |
79 | else:
80 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
81 |
82 |
83 | @app.route('/edit/job', methods=['POST'])
84 | def edit_job():
85 | if request.method == 'POST':
86 |
87 | payload = request.json
88 | if payload['name'] is not None:
89 |
90 | check_data = Job.query.filter_by(
91 | name=payload['name'].lower().strip()).first()
92 | if check_data and check_data.name != payload['name'].lower().strip():
93 | return jsonify({'message': 'Data - '+check_data.name+' already exists.'})
94 | else:
95 | try:
96 | new_data = Job.query.filter_by(
97 | id=payload['id']).first()
98 | new_data.name = payload['name'].lower().strip()
99 | new_data.state = payload['state'].lower().strip()
100 | new_data.country = payload['country'].lower().strip()
101 | db.session.commit()
102 | return jsonify({'success': 'Data Updated'})
103 |
104 | except Exception as e:
105 | print(str(e))
106 |
107 | db.session.rollback()
108 | db.session.close()
109 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
110 | else:
111 | return jsonify({'message': 'Empty Data.'})
112 |
113 | else:
114 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
115 |
116 |
117 |
118 | @app.route('/delete/job/', methods=['POST'])
119 | def delete_job(id):
120 |
121 | try:
122 | new_data = Job.query.filter_by(id = int(id))
123 | if new_data.first():
124 | new_data.delete()
125 | db.session.commit()
126 | return jsonify({'success': "Data Deleted"})
127 |
128 | else:
129 | return jsonify({'message': "Data doesn't exists"})
130 | except Exception as e:
131 | # return Exception for duplicate data
132 | print(str(e))
--------------------------------------------------------------------------------
/back/routes/scraper.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json , db , ma
2 |
3 | from model import Scrape, ScrapeSchema ,City , Tag
4 |
5 | import tasks
6 |
7 | @app.route('/get/scraper', methods=['GET'])
8 | def get_scraper():
9 | if request.method == 'GET':
10 |
11 | data_schema = ScrapeSchema(many=True)
12 | data = Scrape.query.all()
13 | json_data = data_schema.dump(data)
14 | return jsonify(json_data)
15 |
16 |
17 | @app.route('/run/scraper/', methods=['GET'])
18 | def run_scraper(id):
19 | task = tasks.scrape_google(id)
20 | return jsonify({'success' : 'Scraper started'})
21 |
22 | @app.route('/add/scraper', methods=['POST'])
23 | def add_scraper():
24 | if request.method == 'POST':
25 | payload = request.json
26 | print(payload)
27 | if payload:
28 | try:
29 | if (len(payload['tags']) is not 0):
30 | city = City.query.filter_by(id = int(payload['city'])).first()
31 | check_data = Scrape.query.join(City, Scrape.city).filter(Scrape.city.any(City.id == city.id)).first()
32 | meta = {}
33 | meta['detail'] = str(payload['detail'])
34 | if (check_data):
35 | tags = check_data.tag
36 | check_data.meta = json.dumps(meta)
37 | for item in payload['tags']:
38 | if item not in tags:
39 | data = Tag.query.filter_by(
40 | id=item['id']).first()
41 | check_data.tag.append(data)
42 | else:
43 | new_data = Scrape(city)
44 | meta = {}
45 | meta['detail'] = str(payload['detail'])
46 | new_data.meta = json.dumps(meta)
47 | for item in payload['tags']:
48 | data = Tag.query.filter_by(
49 | id=item['id']).first()
50 | new_data.tag.append(data)
51 | db.session.add(new_data)
52 | db.session.commit()
53 | return jsonify({'success': 'Data Added'})
54 |
55 | except Exception as e:
56 | print(str(e))
57 | db.session.rollback()
58 | db.session.close()
59 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
60 | else:
61 | return jsonify({'message': 'Empty Data.'})
62 |
63 | else:
64 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
65 |
66 |
67 | @app.route('/edit/scraper', methods=['POST'])
68 | def edit_scraper():
69 | if request.method == 'POST':
70 |
71 | payload = request.json
72 | if payload['name'] is not None:
73 |
74 | check_data = Scrape.query.filter_by(
75 | name=payload['name'].lower().strip()).first()
76 | if check_data and check_data.name != payload['name'].lower().strip():
77 | return jsonify({'message': 'Data - '+check_data.name+' already exists.'})
78 | else:
79 | try:
80 | new_data = Scrape.query.filter_by(
81 | id=payload['id']).first()
82 | new_data.name = payload['name'].lower().strip()
83 | new_data.state = payload['state'].lower().strip()
84 | new_data.country = payload['country'].lower().strip()
85 | db.session.commit()
86 | return jsonify({'success': 'Data Updated'})
87 |
88 | except Exception as e:
89 | print(str(e))
90 |
91 | db.session.rollback()
92 | db.session.close()
93 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
94 | else:
95 | return jsonify({'message': 'Empty Data.'})
96 |
97 | else:
98 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
99 |
100 |
101 | @app.route('/delete/scraper/', methods=['POST'])
102 | def delete_scraper(id):
103 | if request.method == 'POST':
104 | payload = request.json
105 | check_data = Scrape.query.filter_by(id=int(id))
106 | if check_data.first():
107 | try:
108 | check_data.delete()
109 | db.session.commit()
110 | return jsonify({'success': 'Data deleted'})
111 | except Exception as e:
112 | db.session.rollback()
113 | db.session.close()
114 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
115 |
116 | else:
117 | return jsonify({'message': 'Data does not exist.'})
118 |
119 | else:
120 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
121 |
--------------------------------------------------------------------------------
/back/routes/settings.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json , db , ma
2 | import shutil , os
3 |
4 | @app.route('/settings/clearlogins/whatsapp', methods=['POST'])
5 | def clear_login_wp():
6 | user_data = os.path.abspath("./user-data-wp")
7 | print(user_data)
8 | try:
9 | shutil.rmtree(user_data)
10 | return jsonify({'success': 'Login Cleared'})
11 | except Exception as e:
12 | print(str(e))
13 | return jsonify({'success': 'Login Cleared'})
14 |
15 | @app.route('/settings/clearlogins/sms', methods=['POST'])
16 | def clear_login_sms():
17 | user_data = os.path.abspath("./user-data-sms")
18 | print(user_data)
19 | try:
20 | shutil.rmtree(user_data)
21 | return jsonify({'success': 'Login Cleared'})
22 | except Exception as e:
23 | print(str(e))
24 | return jsonify({'success': 'Login Cleared'})
25 |
26 |
--------------------------------------------------------------------------------
/back/routes/tag.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json
2 | from app import db
3 | from app import ma
4 |
5 | from model import Tag, TagSchema
6 |
7 | @app.route('/get/tag', methods=['GET'])
8 | def get_tag():
9 | if request.method == 'GET':
10 |
11 | data_schema = TagSchema(many=True)
12 | data = Tag.query.all()
13 | json_data = data_schema.dump(data)
14 | return jsonify(json_data)
15 |
16 |
17 | @app.route('/add/tag', methods=['POST'])
18 | def add_tag():
19 | if request.method == 'POST':
20 | payload = request.json
21 | print(payload)
22 | if len(payload['name']) != 0:
23 |
24 | check_data = Tag.query.filter_by(name=payload['name'].lower().strip())
25 | if check_data.first():
26 | return jsonify({'message': 'Data - '+check_data.first().name+' already exists.'})
27 | else:
28 | try:
29 |
30 | new_data = Tag(
31 | payload['name'].lower().strip())
32 |
33 | db.session.add(new_data)
34 | db.session.commit()
35 | json_data = { 'id' : new_data.id , 'name' : new_data.name}
36 | return jsonify({'success': 'Data Added', 'data' : json_data})
37 |
38 | except Exception as e:
39 | print(str(e))
40 | db.session.rollback()
41 | db.session.close()
42 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
43 | else:
44 | return jsonify({'message': 'Empty Data.'})
45 |
46 | else:
47 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
48 |
49 |
50 | @app.route('/edit/tag', methods=['POST'])
51 | def edit_tag():
52 | if request.method == 'POST':
53 |
54 | payload = request.json
55 | if payload['name'] is not None:
56 |
57 | check_data = Tag.query.filter_by(
58 | name=payload['name'].lower().strip()).first()
59 | if check_data and check_data.name != payload['name'].lower().strip():
60 | return jsonify({'message': 'Data - '+check_data.name+' already exists.'})
61 | else:
62 | try:
63 | new_data = Tag.query.filter_by(
64 | id=payload['id']).first()
65 | new_data.name = payload['name'].lower().strip()
66 | db.session.commit()
67 | return jsonify({'success': 'Data Updated'})
68 |
69 | except Exception as e:
70 | print(str(e))
71 |
72 | db.session.rollback()
73 | db.session.close()
74 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
75 | else:
76 | return jsonify({'message': 'Empty Data.'})
77 |
78 | else:
79 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
80 |
81 |
82 | @app.route('/delete/tag', methods=['POST'])
83 | def delete_tag():
84 | if request.method == 'POST':
85 | payload = request.json
86 | check_data = Tag.query.filter_by(id=payload['id'])
87 | if check_data.first():
88 | if len(check_data.first().tag_contact) is int(0):
89 | try:
90 | check_data.delete()
91 | db.session.commit()
92 | return jsonify({'success': 'Data deleted'})
93 | except Exception as e:
94 | db.session.rollback()
95 | db.session.close()
96 | return jsonify({'message': 'Something unexpected happened. Check logs', 'log': str(e)})
97 | else:
98 | return jsonify({'message': 'Cannot delete data. Being used by other master.'})
99 |
100 | else:
101 | return jsonify({'message': 'Data does not exist.'})
102 |
103 | else:
104 | return jsonify({'message': 'Invalid HTTP method . Use POST instead.'})
105 |
--------------------------------------------------------------------------------
/back/routes/templates.py:
--------------------------------------------------------------------------------
1 | from app import app , jsonify , request , json , UPLOAD_FOLDER , IntegrityError
2 | from app import db
3 | from app import ma
4 | import os , shutil
5 |
6 | from model import Template , TemplateSchema
7 |
8 | @app.route('/get/template', methods=['GET'])
9 | def get_template():
10 | schema = TemplateSchema(many=True)
11 | data = Template.query.all()
12 | return jsonify(schema.dump(data))
13 |
14 | @app.route('/add/template', methods=['POST'])
15 | def add_template():
16 | payload = json.loads(request.form['data'])
17 | file = request.files
18 | if payload :
19 | try:
20 | new_data = Template(
21 | payload['name'], payload['message'] ,payload['filetype'] )
22 |
23 | if len(file) != 0:
24 | file = request.files['image']
25 | try:
26 |
27 | if file :
28 | filename =file.filename
29 | foldertemp = os.path.join(
30 | UPLOAD_FOLDER, 'templates')
31 |
32 | if os.path.exists(foldertemp):
33 | filetemp = os.path.join(
34 | foldertemp, filename)
35 | file.save(filetemp)
36 | setattr(new_data, 'path', filetemp)
37 | else:
38 |
39 | os.makedirs(foldertemp)
40 |
41 | filetemp = os.path.join(
42 | foldertemp, filename)
43 | file.save(filetemp)
44 | setattr(new_data, 'path', filetemp)
45 | else:
46 | return jsonify({'message': 'Image file not supported.'})
47 |
48 | except IntegrityError as e:
49 | print(str(e))
50 | return jsonify({'message': 'Image file not supported.'})
51 |
52 |
53 | except Exception as e:
54 | print(str(e))
55 |
56 | db.session.add(new_data)
57 | db.session.commit()
58 | return jsonify({'success': "Data added"})
59 |
60 | except Exception as e:
61 | # return Exception for duplicate data
62 | print(str(e))
63 | return jsonify({'message': "Something went wrong"})
64 |
65 |
66 | @app.route('/delete/template/', methods=['POST'])
67 | def delete_template(id):
68 |
69 | try:
70 | new_data = Template.query.filter_by(id = int(id))
71 | if new_data.first():
72 | if len(new_data.first().template_job) is 0:
73 |
74 | new_data.delete()
75 | db.session.commit()
76 | return jsonify({'success': "Data Deleted"})
77 | else:
78 | return jsonify({'message': "Cannot delete - in use by a job"})
79 |
80 | else:
81 | return jsonify({'message': "Data doesn't exists"})
82 | except Exception as e:
83 | # return Exception for duplicate data
84 | print(str(e))
85 |
--------------------------------------------------------------------------------
/back/run.py:
--------------------------------------------------------------------------------
1 | from app import app
2 | from app import db
3 |
4 | if __name__ == "__main__":
5 | db.create_all()
6 | app.run(port= "5050" , debug = False)
--------------------------------------------------------------------------------
/back/scraper/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/back/scraper/__init__.py
--------------------------------------------------------------------------------
/back/task.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/back/task.db
--------------------------------------------------------------------------------
/front/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/front/README.md:
--------------------------------------------------------------------------------
1 | # front
2 |
3 | > Front end for Automato
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | $ npm run install
10 |
11 | # serve with hot reload at localhost:3000
12 | $ npm run dev
13 |
14 | # build for production and launch server
15 | $ npm run build
16 | $ npm run start
17 |
18 | # generate static project
19 | $ npm run generate
20 | ```
21 |
22 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).
23 |
--------------------------------------------------------------------------------
/front/assets/README.md:
--------------------------------------------------------------------------------
1 | # ASSETS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
6 |
7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
8 |
--------------------------------------------------------------------------------
/front/assets/b-style.scss:
--------------------------------------------------------------------------------
1 | $tabs-link-active-color: black ;
2 | @import "~bulma/sass/utilities/_all";
3 | @import "~bulma/sass/base/_all";
4 | @import "~bulma/sass/components/_all";
5 | @import "~bulma/sass/elements/_all";
6 | @import "~bulma/sass/form/_all";
7 |
8 | $speed-slow: 150ms !default;
9 | $speed-slower: 250ms !default;
10 |
11 | @import "~bulma/sass/elements/table.sass";
12 | @import "~bulma/sass/components/tabs.sass";
13 | @import "~bulma/sass/components/dropdown.sass";
14 | @import "~bulma/sass/elements/tag.sass";
15 | @import "~buefy/src/scss/utils/_all.scss";
16 | @import "~buefy/src/scss/components/_checkbox.scss";
17 | @import "~buefy/src/scss/components/_table.scss";
18 | @import "~buefy/src/scss/components/_tabs.scss";
19 | @import "~buefy/src/scss/components/_dropdown.scss";
20 | @import "~buefy/src/scss/components/_notices.scss";
21 | @import "~buefy/src/scss/components/_tag.scss";
22 | @import "~buefy/src/scss/components/_taginput.scss";
23 | @import "~buefy/src/scss/components/_radio.scss";
24 | @import "~buefy/src/scss/components/_autocomplete.scss";
25 |
--------------------------------------------------------------------------------
/front/assets/main.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/assets/main.css
--------------------------------------------------------------------------------
/front/components/EditContact.vue:
--------------------------------------------------------------------------------
1 |
2 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/front/plugins/README.md:
--------------------------------------------------------------------------------
1 | # PLUGINS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
6 |
7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
8 |
--------------------------------------------------------------------------------
/front/plugins/buefy.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/plugins/buefy.js
--------------------------------------------------------------------------------
/front/plugins/feather.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import VueFeather from 'vue-feather';
3 |
4 | Vue.use(VueFeather);
--------------------------------------------------------------------------------
/front/static/README.md:
--------------------------------------------------------------------------------
1 | # STATIC
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your static files.
6 | Each file inside this directory is mapped to `/`.
7 | Thus you'd want to delete this README.md before deploying to production.
8 |
9 | Example: `/static/robots.txt` is mapped as `/robots.txt`.
10 |
11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
12 |
--------------------------------------------------------------------------------
/front/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/favicon.ico
--------------------------------------------------------------------------------
/front/static/main.scss:
--------------------------------------------------------------------------------
1 | $speed-slow: 150ms !default;
2 | $speed-slower: 250ms !default
--------------------------------------------------------------------------------
/front/static/uploads/templates/000000.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/000000.jpg
--------------------------------------------------------------------------------
/front/static/uploads/templates/11111.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/11111.png
--------------------------------------------------------------------------------
/front/static/uploads/templates/700rapid (1).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/700rapid (1).jpg
--------------------------------------------------------------------------------
/front/static/uploads/templates/IMG_4467_1024x1024.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/IMG_4467_1024x1024.webp
--------------------------------------------------------------------------------
/front/static/uploads/templates/KENDRICK_NAVY_F_1024x1024.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/KENDRICK_NAVY_F_1024x1024.webp
--------------------------------------------------------------------------------
/front/static/uploads/templates/image-indiamart-resized.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/image-indiamart-resized.jpg
--------------------------------------------------------------------------------
/front/static/uploads/templates/itmanan_png_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/itmanan_png_white.png
--------------------------------------------------------------------------------
/front/static/uploads/templates/mememe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/mememe.jpg
--------------------------------------------------------------------------------
/front/static/uploads/templates/sign-mememem.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/highoncarbs/automato/5a8a7e8cae613962684c725edf4bf88e324ef775/front/static/uploads/templates/sign-mememem.jpg
--------------------------------------------------------------------------------
/front/store/README.md:
--------------------------------------------------------------------------------
1 | # STORE
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your Vuex Store files.
6 | Vuex Store option is implemented in the Nuxt.js framework.
7 |
8 | Creating a file in this directory automatically activates the option in the framework.
9 |
10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).
11 |
--------------------------------------------------------------------------------