Jelajahi Sumber

Merge branch 'master' of http://git.choozmo.com:3000/choozmo/bhouse_api

deployer 3 tahun lalu
induk
melakukan
456fa89106

+ 5 - 2
models/__init__.py

@@ -11,9 +11,12 @@ def create_app():
 
     from models.contents.routes import contents_app
     from models.manages.routes import manages_app
-    
-    app.register_blueprint(contents_app)
+    from models.store_locations.routes import store_locations_app
+    from models.statics.routes import statics_app
 
+    app.register_blueprint(contents_app)
     app.register_blueprint(manages_app)
+    app.register_blueprint(store_locations_app)
+    app.register_blueprint(statics_app)
 
     return app

+ 7 - 0
models/store_locations/__init__.py

@@ -0,0 +1,7 @@
+from os import path
+from models.config import CONTENT_DIR
+
+
+STORE_CONTENT_DIR = {'north': path.join(CONTENT_DIR, 'store_north'),
+                     'central': path.join(CONTENT_DIR, 'store_central'),
+                     'south': path.join(CONTENT_DIR, 'store_south')}

+ 102 - 0
models/store_locations/routes.py

@@ -0,0 +1,102 @@
+from flask import request, Blueprint
+from flask_restful import Resource, Api
+from os import path, makedirs
+import logging
+from bs4 import BeautifulSoup
+from models.config import CONTENT_DIR
+from models.utils import read_line_md, gen_md_file_dirs, translate, get_now_time
+from models.store_locations.templates import store_location_template, amp_img_template
+from models.store_locations import STORE_CONTENT_DIR
+from models.statics.routes import get_static_imgs_src
+
+
+store_locations_app = Blueprint('store_locations', __name__)
+logger = logging.getLogger(__name__)
+api = Api(store_locations_app)
+
+
+class StoreLocations(Resource):
+    def __init__(self):
+        self.exist_img_file_src = []
+
+    def get_file_data(self, f_dir):
+        result = {}
+        is_amp_img = False
+        for line in read_line_md(f_dir):
+            if 'title: ' in line:
+                result['title'] = line.split('title: ')[-1].replace('"', '').replace('\n', '')
+            elif 'type: ' in line:
+                result['type'] = line.split('type: ')[-1].replace('"', '').replace('\n', '')
+            elif '<amp-img' in line:
+                is_amp_img = True
+            elif 'h2 class="mb-4"' in line:
+                result['store'] = BeautifulSoup(line, 'html.parser').h2.string
+            elif '營業時間 | ' in line:
+                result['hour'] = line.split('營業時間 | ')[-1].replace('\n', '')
+            elif '門市電話 | ' in line:
+                result['phone'] = BeautifulSoup(line, 'html.parser').a.string
+            elif '門市地點 | ' in line:
+                result['location'] = BeautifulSoup(line, 'html.parser').a.string
+            elif '停車資訊 | ' in line:
+                result['parking'] = line.split('停車資訊 | ')[-1].replace('\n', '')
+            if is_amp_img:
+                if 'src=' in line:
+                    img_src = line.split('src=')[-1].replace('"', '').replace('\n', '')
+                    if img_src in self.exist_img_file_src:
+                        result.setdefault('imgs', []).append(img_src)
+                if '</amp-img':
+                    is_amp_img = False
+        return result
+
+    def _get_district_name(self, title):
+        return translate(title.replace('門市', '')).lower().replace(' ', '_')
+
+    def _get_amp_img_md(self, imgs, title):
+        result = ''
+        for img_src in imgs:
+            amp_img_md = amp_img_template.format(src=img_src, title=title)
+            result += amp_img_md + '\n'
+        return result
+
+    def get(self):
+        result = {}
+        for zone, dir_ in STORE_CONTENT_DIR.items():
+            self.exist_img_file_src = get_static_imgs_src('store_{}'.format(zone))
+            for f_dir in gen_md_file_dirs(dir_):
+                result.setdefault(zone, []).append(self.get_file_data(f_dir))
+        return result
+
+    def post(self):
+        def get_store_dir():
+            DISTRICT_STORES = {'store_north': '北部門市',
+                               'store_central': '中部門市',
+                               'store_south': '南部門市',
+                               'store_east': '東部門市'}
+            return path.join(CONTENT_DIR,
+                             store_data.get('type'),
+                             DISTRICT_STORES.get(store_data.get('type'), '中部門市'),
+                             store_data.get('title').replace('門市', ''))
+
+        update_md = []
+        for store_data in request.json:
+            store_location_md = store_location_template.format(
+                title=store_data.get('title'),
+                date=get_now_time(),
+                type=store_data.get('type'),
+                district=self._get_district_name(store_data.get('title')),
+                amp_imgs=self._get_amp_img_md(store_data.get('imgs'), store_data.get('title')),
+                store=store_data.get('store'),
+                hour=store_data.get('hour'),
+                phone_without_dash=store_data.get('phone').replace('-', ''),
+                phone=store_data.get('phone'),
+                location=store_data.get('location'),
+                parking=store_data.get('parking'))
+            store_dir = get_store_dir()
+            makedirs(store_dir, exist_ok=True)
+            with open(path.join(store_dir, 'index.md'), 'w') as md:
+                md.write(store_location_md)
+            update_md.append(store_location_md)
+        return update_md
+
+
+api.add_resource(StoreLocations, '/api/store_locations')

+ 58 - 0
models/store_locations/templates.py

@@ -0,0 +1,58 @@
+store_location_template = '''---
+title: "{title}"
+date: {date}
+lastmod: {date}
+draft: false
+type: "{type}"
+url: "/{type}/bhouse_store_in_{district}_city"
+image: ""
+tags:
+---
+
+<section class="section12">
+  <div class="container">
+    <div class="row">
+      <div class="col-md-5 col-sm-12">
+        <div class="block">
+          <div class="section-title">
+            <amp-carousel
+              class="mb-5"
+              width="450"
+              height="300"
+              layout="responsive"
+              type="slides"
+              autoplay
+              delay="2500"
+              role="region"
+              aria-label="小寶優居 | 台中門市">
+{amp_imgs}            </amp-carousel>
+          </div>
+        </div>
+      </div>
+      <div class="col-md-7 col-sm-12">
+        <div class="block ms-md-5 mb-5">
+          <h2 class="mb-4">​{store}</h2>
+          <div>
+            營業時間 | {hour}
+          </div>
+          <div>
+            門市電話 | <a href="tel:{phone_without_dash}">{phone}</a>
+          </div>
+          <div>
+            門市地點 | <a href="https://www.google.com/maps?q={location}" target="_blank">{location}</a>
+          </div>
+          <div class="mb-5">
+            停車資訊 | {parking}
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</section>'''
+
+
+amp_img_template = '''              <amp-img src="{src}"
+                width="450"
+                height="300"
+                layout="responsive"
+                alt="小寶優居 | {title}"></amp-img>'''

+ 22 - 0
models/utils/__init__.py

@@ -1,3 +1,8 @@
+from os import path, walk
+from googletrans import Translator
+from datetime import datetime, timezone, timedelta
+
+
 def write_md(f_dir, content):
     with open(f_dir, 'w') as md:
         md.write(content)
@@ -7,3 +12,20 @@ def read_line_md(f_dir):
     with open(f_dir, 'r') as md:
         pre_content = md.readlines()
     return pre_content
+
+
+def gen_md_file_dirs(dir_):
+    for root, dirs, files in walk(dir_):
+        for f in files:
+            if '.md' not in f:
+                continue
+            yield path.join(root, f)
+
+
+def translate(text):
+    result = Translator().translate(text, dest='en')
+    return result.text
+
+
+def get_now_time():
+    return datetime.now(timezone(timedelta(hours=+8))).isoformat(timespec="seconds")

+ 16 - 1
requirements.txt

@@ -1,9 +1,23 @@
 aniso8601==9.0.1
 beautifulsoup4==4.9.3
+certifi==2020.12.5
+chardet==3.0.4
 click==7.1.2
+contextvars==2.4
+flake8==3.9.0
 Flask==1.1.2
 Flask-Cors==3.0.10
 Flask-RESTful==0.3.8
+googletrans==3.1.0a0
+h11==0.9.0
+h2==3.2.0
+hpack==3.0.0
+hstspreload==2020.12.22
+httpcore==0.9.1
+httpx==0.13.3
+hyperframe==5.2.0
+idna==2.10
+immutables==0.15
 importlib-metadata==3.10.0
 itsdangerous==1.1.0
 Jinja2==2.11.3
@@ -12,9 +26,10 @@ mccabe==0.6.1
 pycodestyle==2.7.0
 pyflakes==2.3.1
 pytz==2021.1
+rfc3986==1.4.0
 six==1.15.0
+sniffio==1.2.0
 soupsieve==2.2.1
 typing-extensions==3.7.4.3
 Werkzeug==1.0.1
-flake8==3.9.0
 zipp==3.4.1