Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Preprocessor #19

Open
wants to merge 13 commits into
base: new-feline
Choose a base branch
from
Empty file added src/__init__.py
Empty file.
233 changes: 233 additions & 0 deletions src/csstree/BlockSplitter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
from __future__ import print_function
import re
import logging
import logging_config #to configure logging, no calls needed
from CssTree.CssElement import CssElement



MIN_ZOOM = 1
MAX_ZOOM = 19


BLOCK_SPLITTER = re.compile(r'([^@{]*)\s*\{(.*?)\}', re.DOTALL | re.MULTILINE)
ZOOM = re.compile(r'(.*?)(\|z[\d\-]*?)?(\[.*)?') #deprecated
ONE_ZOOM = re.compile(r'(\d{1,2})$')
ZOOM_RANGE = re.compile(r'(\d{1,2})-(\d{1,2})$')
ZOOM_TO_MAX = re.compile(r'(\d{1,2})-$')


TAG_RE = re.compile(r'(^.*?)[\|\[$:]', re.MULTILINE)
ZOOM_RE = re.compile(r'.*?\|z([\d\-]*?)[\[$:]')
SELECTORS_RE = re.compile(r'(\[.*?\])')
SUB_RE = re.compile(r'.*:(.*)$')
ONE_SELECTOR_RE = re.compile(r'\[(.*?)\]')


class BlockSplitter:
"""
Should also be initializeable by a preprocessed file
Copy link

@mgsergio mgsergio May 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В докстрингах хорошо писать документацию к классу, а это больше похоже на коммент.

"""
def __init__(self, preprocessed_blocks, write_to_file=False):
print("Num of input blocks is: {}".format(len(preprocessed_blocks)))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Разве это не в лог должно идти?
Ты ж импортил логгинг.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Убрал вообще

self.blocks = preprocessed_blocks
self.split_blocks = {} # selector : list of attributes
self.write_to_file = write_to_file
self.blocks_by_zoom_level = self.init_blocks_by_zoom_level()
print("Will write to file! {}".format(self.write_to_file))


def init_blocks_by_zoom_level(self):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Название функции не очень соответсвует тому, что она делает, лучше get или make...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется, вполне соответствует. Т.к. я из init часть функционала выношу в отдельную функцию, т.е., одну из переменных инициализирую в отдельной функции. Но в новом варианте обошелся без дополнительной функции вообще.

ret = {}
for i in range(MIN_ZOOM, MAX_ZOOM + 1):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xrange is better, it does not create sequence.
You can think of it as a lazy sequence.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Уже нету

ret[i] = {} #tag with selectors and subclasses to attributes, selectors must be sorted
return ret


def process(self):
self.split_commas()
self.process_blocks_by_zoom_level()

if self.write_to_file:
self.write()
pass

def process_blocks_by_zoom_level(self):
for zoom in self.blocks_by_zoom_level:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for zoom, block in self.blocks_by_zoom_level.iter_items():
   self.process_zoom_level(zoom, block)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вроде поправлял. Если где-то не поправил, то, когда будут попадаться, буду заменять.

self.process_zoom_level(zoom, self.blocks_by_zoom_level[zoom])

def process_zoom_level(self, zoom, block):
clean_block = [] # another list of tuples
block_keys = sorted(block.keys())
# block.sort(key=lambda x: x[1]) #sort the tuples by the 0th element
old_block = ("", [])
for tag in block_keys:
attrs = block[tag]
if tag == old_block[0] :
old_block[1].extend(attrs)
else:
if old_block[0]:
clean_block.append(old_block)
old_block = (tag, attrs)
self.blocks_by_zoom_level[zoom] = clean_block


def clean_split_by(self, string, separator):
return filter(lambda x: x != "", map(lambda x: x.strip(), string.split(separator)))


def all_zooms_in_css_range(self, str_range):
min_zoom = -1
max_zoom = -1

if not str_range:
min_zoom = MIN_ZOOM
max_zoom = MAX_ZOOM

elif ONE_ZOOM.match(str_range):
min_zoom = int(str_range)
max_zoom = min_zoom

elif ZOOM_TO_MAX.match(str_range):
min_zoom = int(str_range[:-1])
max_zoom = MAX_ZOOM

elif ZOOM_RANGE.match(str_range):
found = ZOOM_RANGE.findall(str_range)[0]
(min_zoom, max_zoom) = map(lambda x: int(x), found)

if max_zoom < 0 or min_zoom < 0 or max_zoom < min_zoom:
raise Exception("Failed to parse the zoom levels")

max_zoom = MAX_ZOOM if max_zoom > MAX_ZOOM else max_zoom
min_zoom = MIN_ZOOM if min_zoom < MIN_ZOOM else min_zoom

ret = [i for i in range(min_zoom, max_zoom + 1)]

return ret




def split_keys_by_zoom(self, keys):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 отступа! Жуть как много

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Промахнулся, это должно быть на строке выше.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отступы со временем тоже будут уходить.

ret = []
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В "длинных" функциях лучше давать более говорящие имена локальным переменным,
чтобы было ясно что в них хранится.

for key in keys:
parts_list = ZOOM.findall(key)
if not parts_list:
print("Unparseable key {}".format(key))
continue
parts = parts_list[0]
if parts:
selector, zoom = parts[0], (parts[1] if not parts else parts[1][2:])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

как так?? parts[1] if not parts ...

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пофиксил

print(">>>> {} : {}".format(selector, zoom))
all_zooms = self.all_zooms_in_css_range(zoom)
ret.append(map(lambda x: (selector, x), all_zooms))
else:
print("Got an unparseable node and zoom level: {}".format(key))
logging.warning("NOTE THAT THIS TAG HAS BEEN IGNORED AND MUST BE PROCESSED IN THE FUTURE!")
return ret


# to be refactored
def split_commas(self):
for block, imported_from in self.blocks:
found = BLOCK_SPLITTER.findall(block)
for entry in found:
keys = self.clean_split_by(entry[0], ",")
attributes = sorted(self.clean_split_by(entry[1], ";"))

last_attr = ""
clean_attributes = []
for a in attributes:
if a == last_attr:
logging.warning("Duplicate attribute {} for tag/zoom {} imported from {}".format(a, keys, imported_from))
continue
clean_attributes.append(a)



Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опять куча отступов.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Буду убирать со временем. По-моему, кстати, везде поправил

for key in keys:
elements = self.css_key_factory(key)

for element in elements:
subclass = "::{}".format(element.subclass) if element.subclass else ""
resulting_tag = "{}{}{}".format(element.tag, "".join(sorted(element.selectors)), subclass)

if resulting_tag in self.blocks_by_zoom_level[element.zoom]:
filtered_attributes = []
for a in clean_attributes:
if a in self.blocks_by_zoom_level[element.zoom][resulting_tag]:
print("Duplicate attribute {} for tag {} on zoom {} imported from {}".format(a, resulting_tag, element.zoom, imported_from))
else:
filtered_attributes.append(a)

self.blocks_by_zoom_level[element.zoom][resulting_tag].update(self.map_attrs_to_import_source(filtered_attributes, imported_from))
else:
self.blocks_by_zoom_level[element.zoom][resulting_tag] = self.map_attrs_to_import_source(clean_attributes, imported_from)


def map_attrs_to_import_source(self, attributes, imported_from):
return dict(map(lambda x: (x, imported_from), attributes))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return {x: imported_from for x in attributes}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Пока не трогал.



def write(self):
print("Writing split blocks by zoom, num blocks {}".format(len(self.blocks_by_zoom_level)))
with open("../../out/split_by_commas.mapcss", "w") as out_file:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use os.path.join

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Да, подобные штуки будут заменяться с хардкода на параметры.

for zoom in sorted(self.blocks_by_zoom_level.keys()):
blocks = self.blocks_by_zoom_level[zoom]
# for zoom, blocks in self.blocks_by_zoom_level:
out_file.write(" /* ===== ZOOM {} ===== */\n\n".format(zoom))

for tag, attrs in blocks:
out_file.write("{} {{\n".format(tag))
for attr in attrs:
out_file.write(" {}; /* == {} == */\n".format(attr, attrs[attr]))
# out_file.write(attrs)
out_file.write("}\n\n")


def css_key_factory(self, str_key):
# type: (str) -> [CssElement]
tag_list = TAG_RE.findall(str_key)
tag = tag_list[0] if tag_list else str_key

zoom_list = ZOOM_RE.findall(str_key)
zoom = zoom_list[0] if zoom_list else ""

# if "][" in str_key:
# print("Str key contains ][")

selectors_list = SELECTORS_RE.findall(str_key)
# selectors = selectors_list[0] if selectors_list else ""

str_key = TAG_RE.sub("", str_key)
str_key = ZOOM_RE.sub("", str_key)
str_key = SELECTORS_RE.sub("", str_key)

subclass_list = SUB_RE.findall(str_key)
subclass = subclass_list[0] if subclass_list else ""
all_zooms = self.all_zooms_in_css_range(zoom)
ret = map(lambda z: CssElement(tag, z, selectors_list, subclass), all_zooms)
return ret






if __name__ == "__main__":
blockSplitter = BlockSplitter([])
# print(blockSplitter.all_zooms_in_css_range("10"))
# print(blockSplitter.all_zooms_in_css_range("10-"))
# print(blockSplitter.all_zooms_in_css_range("10-12"))
# print(blockSplitter.all_zooms_in_css_range("10-25"))

# print(blockSplitter.split_key_by_components("*::*"))
# print(blockSplitter.split_key_by_components("*"))
# print(blockSplitter.split_key_by_components("*|z12"))
# print(blockSplitter.split_key_by_components("*::int_name "))
# print(blockSplitter.split_key_by_components("line|z5[highway=world_level]"))
# print(blockSplitter.css_key_factory("line|z17-18[highway=footway][tunnel?]::tunnelBackground"))



15 changes: 15 additions & 0 deletions src/csstree/CssTree/CssElement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from __future__ import print_function

import logging
import logging.config


class CssElement:
def __init__(self, tag, zoom, selectors, subclass):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/subclass/css_subclass/

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Подумаю

self.tag = tag
self.zoom = zoom
self.selectors = selectors #[]
self.subclass = subclass

def __repr__(self):
return "{}|z{}::{}".format(self.tag, self.zoom, self.subclass)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add newline

35 changes: 35 additions & 0 deletions src/csstree/CssTree/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import print_function

import logging
import CssElement




class CssTree:
def __init__(self, min_zoom=1, max_zoom=19):
self.subtrees_by_zoom = {}
for i in range(min_zoom, max_zoom + 1):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

xrange

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.subtrees_by_zoom = {i: CssSubtree() for i in xrange(min_zoom, max_zoom + 1)}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

По-моему, поменял на какой-то однострочник

self.subtrees_by_zoom[i] = CssSubtree()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

отступ, кажется, не нужен, да и pass тоже

pass

def add(self, csselement):
self.subtrees_by_zoom[csselement.zoom].add(csselement)
a = CssElement("a", "b", "c", "d")
Copy link

@mgsergio mgsergio May 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a is unused

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Делался для дебага, по-моему, убрал





class CssSubtree:
def __init__(self):
self.branches_by_tag = {}
pass

def add(self, csselement):
pass


class CssNode:
def __init__(self):
pass
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newline

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ок

Loading