From cb6c74ab60b82a95bec5e8391ebfc4e7bf1a6861 Mon Sep 17 00:00:00 2001 From: Kyle King Date: Thu, 13 Jun 2019 21:15:35 -0400 Subject: [PATCH] Fix: assets paths --- pages/02-Dash-Layout.py | 6 ++-- pages/0201-Dash-Core-Components.py | 48 ++++++++++++------------- pages/020201-Dash-DataTable.py | 14 ++++---- pages/020202-Dash-Sortable-DataTable.py | 4 ++- pages/020203-Dash-Backend-Datatable.py | 4 ++- pages/0203-Dash-Tabs.py | 2 ++ pages/assets/_external_scripts.txt | 2 +- pages/assets/styles.css | 8 +++++ pages/modules/distortion_chart.py | 3 +- pages/modules/pareto_chart.py | 4 ++- poetry.lock | 16 +++++---- pyproject.toml | 2 +- requirements.txt | 16 ++++----- 13 files changed, 76 insertions(+), 53 deletions(-) diff --git a/pages/02-Dash-Layout.py b/pages/02-Dash-Layout.py index de211a8..2349d67 100644 --- a/pages/02-Dash-Layout.py +++ b/pages/02-Dash-Layout.py @@ -19,7 +19,9 @@ import pandas as pd import plotly.graph_objs as go -app = dash.Dash(__name__) +assets_dir = Path.cwd() / 'pages/assets' + +app = dash.Dash(__name__, assets_folder=str(assets_dir)) colors = { 'background': '#FFF', @@ -46,8 +48,6 @@ def randY(): # ===================================================================================================================== # Table -assets_dir = Path('assets') - agricDF = pd.read_csv(assets_dir / 'usa-agricultural-exports-2011.csv') diff --git a/pages/0201-Dash-Core-Components.py b/pages/0201-Dash-Core-Components.py index 4797d0a..69c4134 100644 --- a/pages/0201-Dash-Core-Components.py +++ b/pages/0201-Dash-Core-Components.py @@ -12,13 +12,14 @@ """ from datetime import datetime as dt +from pathlib import Path import dash import dash_core_components as dcc import dash_html_components as html import plotly.graph_objs as go -app = dash.Dash(__name__) +app = dash.Dash(__name__, assets_folder=str(Path.cwd() / 'pages/assets')) # ===================================================================================================================== # Layout the application @@ -33,7 +34,7 @@ # Dropdowns # ========== - html.Label('Dropdown', style={'margin-top': '20px'}), + html.Label('Dropdown'), dcc.Dropdown( options=[ {'label': 'New York City', 'value': 'NYC'}, @@ -43,7 +44,7 @@ value='MTL', ), - html.Label('Multi-Select Dropdown', style={'margin-top': '20px'}), + html.Label('Multi-Select Dropdown'), dcc.Dropdown( options=[ {'label': 'New York City', 'value': 'NYC'}, @@ -58,7 +59,7 @@ # Sliders # ========== - html.Label('Basic Slider', style={'margin-top': '20px'}), + html.Label('Basic Slider'), dcc.Slider( id='example-id', min=0, @@ -67,7 +68,7 @@ value=3.3, updatemode='drag', # Faster updates ), - html.Label('Labeled Slider', style={'margin-top': '20px'}), + html.Label('Labeled Slider'), dcc.Slider( min=0, max=10, @@ -76,7 +77,7 @@ dots=False, ), - html.Label('Range Slider With Nice Labels (No Inclusion)', style={'margin-top': '45px'}), + html.Label('Range Slider With Nice Labels (No Inclusion)'), dcc.RangeSlider( min=0, max=100, @@ -89,9 +90,8 @@ }, included=False, ), - html.Label('Pushable Multi-Range Slider', style={'margin-top': '45px'}), + html.Label('Pushable Multi-Range Slider'), dcc.RangeSlider( - marks={i: i for i in range(-5, 30)}, min=-5, max=30, value=[1, 3, 4, 5, 12, 17], @@ -102,7 +102,7 @@ # Inputs # ========== - html.Label('Input and Text Area', style={'margin-top': '45px'}), + html.Label('Input and Text Area'), dcc.Input( placeholder='Enter a value...', type='text', @@ -118,7 +118,7 @@ # Select (Radio/Checkbox) # ========== - html.Label('Radio Items', style={'margin-top': '20px'}), + html.Label('Radio Items'), dcc.RadioItems( options=[ {'label': 'New York City', 'value': 'NYC'}, @@ -128,7 +128,7 @@ value='MTL', # labelStyle={'display': 'inline-block'}, ), - html.Label('Checkboxes', style={'margin-top': '20px'}), + html.Label('Checkboxes'), dcc.Checklist( options=[ {'label': 'New York City', 'value': 'NYC'}, @@ -142,30 +142,30 @@ # Button and Callback # ========== - html.Label('Example Button with Callback', style={'margin-top': '20px'}), + html.Label('Example Button with Callback'), html.Div(dcc.Input(id='input-box', type='text')), html.Button('Submit', id='button'), html.Div( id='output-container-button', - children='Enter a value and press submit' + children='Enter a value and press submit', ), # ========== # Datepicker # ========== - html.Label('Single Date Picker', style={'margin-top': '20px'}), + html.Label('Single Date Picker'), dcc.DatePickerSingle( id='date-picker-single', date=dt(2020, 4, 1), ), - html.Label('Ranged Date Picker', style={'margin-top': '20px'}), + html.Label('Ranged Date Picker'), dcc.DatePickerRange( id='date-picker-range', start_date=dt(1997, 5, 3), - end_date_placeholder_text='Select a date!' + end_date_placeholder_text='Select a date!', ), - html.H6('^ fyi: need padding below for dropdown element', style={'margin-top': '20px'}), + html.H6('^ fyi: need padding below for dropdown element'), # ========== # Graphs - of course @@ -180,8 +180,8 @@ y=[219, 99, 97, 112, 127, 180, 236, 207, 236, 263, 350, 430, 474, 526, 488, 537, 500, 439], name='Rest of world', marker=go.bar.Marker( - color='rgb(55, 83, 109)' - ) + color='rgb(55, 83, 109)', + ), ), go.Bar( x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, @@ -189,19 +189,19 @@ y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270, 299, 340, 403, 549, 499], name='China', marker=go.bar.Marker( - color='rgb(26, 118, 255)' - ) - ) + color='rgb(26, 118, 255)', + ), + ), ], layout=go.Layout( title='US Export of Plastic Scrap', showlegend=True, legend=go.layout.Legend( x=0, - y=1.0 + y=1.0, ), margin=go.layout.Margin(l=40, r=0, t=40, b=30), # noqa: E741 - ) + ), ), style={'height': 300}, id='my-graph', diff --git a/pages/020201-Dash-DataTable.py b/pages/020201-Dash-DataTable.py index 267f843..2a8895d 100644 --- a/pages/020201-Dash-DataTable.py +++ b/pages/020201-Dash-DataTable.py @@ -7,6 +7,8 @@ """ +from pathlib import Path + import dash import dash_html_components as html import dash_table @@ -15,7 +17,7 @@ df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/26k-consumer-complaints.csv') df = df[:45] -app = dash.Dash(__name__) +app = dash.Dash(__name__, assets_folder=str(Path.cwd() / 'pages/assets')) # ===================================================================================================================== # Layout the application @@ -27,8 +29,8 @@ html.H2(children='Example DataTable'), dash_table.DataTable( id='table', - columns=[{"name": i, "id": i} for i in df.columns], - data=df.to_dict("rows"), + columns=[{'name': i, 'id': i} for i in df.columns], + data=df.to_dict('rows'), # # > Setting a fixed row can cause the header row to have the wrong horizontal spacing as the data # n_fixed_rows=1, style_table={ @@ -55,9 +57,9 @@ }], merge_duplicate_headers=True, # style_as_list_view=True, # Remove vertical lines for short tables - ) - ] - ) + ), + ], + ), ]) if __name__ == '__main__': diff --git a/pages/020202-Dash-Sortable-DataTable.py b/pages/020202-Dash-Sortable-DataTable.py index 1639553..9e4b3fe 100644 --- a/pages/020202-Dash-Sortable-DataTable.py +++ b/pages/020202-Dash-Sortable-DataTable.py @@ -5,6 +5,8 @@ """ +from pathlib import Path + import dash import dash_core_components as dcc import dash_html_components as html @@ -14,7 +16,7 @@ df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv') -app = dash.Dash(__name__) +app = dash.Dash(__name__, assets_folder=str(Path.cwd() / 'pages/assets')) # ===================================================================================================================== # Layout the application diff --git a/pages/020203-Dash-Backend-Datatable.py b/pages/020203-Dash-Backend-Datatable.py index d9f8511..1b62892 100644 --- a/pages/020203-Dash-Backend-Datatable.py +++ b/pages/020203-Dash-Backend-Datatable.py @@ -7,6 +7,8 @@ """ +from pathlib import Path + import dash import dash_core_components as dcc import dash_html_components as html @@ -14,7 +16,7 @@ import pandas as pd from dash.dependencies import Input, Output -app = dash.Dash(__name__) +app = dash.Dash(__name__, assets_folder=str(Path.cwd() / 'pages/assets')) df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv') diff --git a/pages/0203-Dash-Tabs.py b/pages/0203-Dash-Tabs.py index cb35798..8d42aca 100644 --- a/pages/0203-Dash-Tabs.py +++ b/pages/0203-Dash-Tabs.py @@ -5,6 +5,8 @@ """ +from pathlib import Path + import dash import dash_core_components as dcc import dash_html_components as html diff --git a/pages/assets/_external_scripts.txt b/pages/assets/_external_scripts.txt index 6a9cbf1..08f5c79 100644 --- a/pages/assets/_external_scripts.txt +++ b/pages/assets/_external_scripts.txt @@ -1,4 +1,4 @@ # (This line is a comment, all non-comments lines must be URLs) # List all external files necessary for the Dash app. This will be downloaded and bundled with the PyInstaller output # During development, these links will be based as CLI arguments to the app, so the app can live reload as you edit the source files in this folder -https://codepen.io/chriddyp/pen/bWLwgP.css +# https://codepen.io/chriddyp/pen/bWLwgP.css diff --git a/pages/assets/styles.css b/pages/assets/styles.css index 0d254ff..185a623 100644 --- a/pages/assets/styles.css +++ b/pages/assets/styles.css @@ -7,3 +7,11 @@ body { margin-right: auto; margin-left: auto; } + +._dash-undo-redo { + display: none; +} + +.rc-slider { + min-height: 45px; +} diff --git a/pages/modules/distortion_chart.py b/pages/modules/distortion_chart.py index 237b191..27ba428 100644 --- a/pages/modules/distortion_chart.py +++ b/pages/modules/distortion_chart.py @@ -7,6 +7,7 @@ """ import math +from pathlib import Path import dash import dash_core_components as dcc @@ -15,7 +16,7 @@ import plotly.graph_objs as go from dash.dependencies import Input, Output -app = dash.Dash(__name__) +app = dash.Dash(__name__, assets_folder=str(Path.cwd() / 'pages/assets')) app.layout = html.Div( className='app-content', diff --git a/pages/modules/pareto_chart.py b/pages/modules/pareto_chart.py index f738576..5b17da7 100644 --- a/pages/modules/pareto_chart.py +++ b/pages/modules/pareto_chart.py @@ -5,13 +5,15 @@ """ +from pathlib import Path + import dash import dash_core_components as dcc import dash_html_components as html import pandas as pd import plotly.graph_objs as go -app = dash.Dash(__name__) +app = dash.Dash(__name__, assets_folder=str(Path.cwd() / 'pages/assets')) def createPareto(df, ylabel='Measurement (units)', colors=('#62A4D1', '#C5676B')): diff --git a/poetry.lock b/poetry.lock index 46dc34e..33edfff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -84,6 +84,10 @@ dash_renderer = "0.24.0" flask-compress = "*" plotly = "*" +[package.source] +reference = "52b473b78082cda0b89afc122882cd5d4c755d66" +type = "git" +url = "https://github.com/plotly/dash" [[package]] category = "main" description = "Dash UI core component suite" @@ -357,7 +361,7 @@ description = "Read metadata from Python packages" name = "importlib-metadata" optional = false python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" -version = "0.17" +version = "0.18" [package.dependencies] zipp = ">=0.5" @@ -634,7 +638,7 @@ description = "pytest: simple powerful testing with Python" name = "pytest" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "4.6.2" +version = "4.6.3" [package.dependencies] atomicwrites = ">=1.0" @@ -815,7 +819,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["pathlib2", "contextlib2", "unittest2"] [metadata] -content-hash = "07b4df56bf84f6e4c9da805da9ca1b53ea9f8faad3f403c763469f6bb182ad3d" +content-hash = "faf25c92ae36fb92d648350809feb771f3e47717f321fb23d20951ef10a909b8" python-versions = "^3.7" [metadata.hashes] @@ -827,7 +831,7 @@ click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b cloudpickle = ["603244e0f552b72a267d47a7d9b347b27a3430f58a0536037a290e7e0e212ecf", "b8ba7e322f2394b9bbbdc1c976e6442c2c02acc784cb9e553cee9186166a6890"] colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] coverage = ["0c5fe441b9cfdab64719f24e9684502a59432df7570521563d7b1aff27ac755f", "2b412abc4c7d6e019ce7c27cbc229783035eef6d5401695dccba80f481be4eb3", "3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9", "39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74", "3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390", "42692db854d13c6c5e9541b6ffe0fe921fe16c9c446358d642ccae1462582d3b", "465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8", "48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe", "4ec30ade438d1711562f3786bea33a9da6107414aed60a5daa974d50a8c2c351", "5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf", "5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e", "6899797ac384b239ce1926f3cb86ffc19996f6fa3a1efbb23cb49e0c12d8c18c", "68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741", "6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09", "7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd", "7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034", "839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420", "8e679d1bde5e2de4a909efb071f14b472a678b788904440779d2c449c0355b27", "8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c", "932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab", "93f965415cc51604f571e491f280cff0f5be35895b4eb5e55b47ae90c02a497b", "988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba", "998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e", "9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609", "9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2", "a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49", "a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b", "a9abc8c480e103dc05d9b332c6cc9fb1586330356fc14f1aa9c0ca5745097d19", "aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d", "bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce", "bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9", "c22ab9f96cbaff05c6a84e20ec856383d27eae09e511d3e6ac4479489195861d", "c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4", "c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773", "c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723", "ca58eba39c68010d7e87a823f22a081b5290e3e3c64714aac3c91481d8b34d22", "df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c", "f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f", "f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1", "f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260", "fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a"] -dash = ["8707d77f8d9c43c83e32a5ecbe16cf42e0ba19b99e9eb7dd491f82afb93ae577"] +dash = [] dash-core-components = ["5e145d07e59b7562dc96e53d70ff9f846ca8793857bc5e6f305cd991e2a633cb"] dash-daq = ["33851d22e46f73610c8e59d50442edacaf1993d457c45f5841b2707e6a1c928c"] dash-html-components = ["7767147eda48560649701d13bfa4c38e9c1f406368625b304cc3d401703a43eb"] @@ -853,7 +857,7 @@ flask = ["ad7c6d841e64296b962296c2c2dabc6543752985727af86a975072dea984b6f3", "e7 flask-compress = ["468693f4ddd11ac6a41bca4eb5f94b071b763256d54136f77957cfee635badb3"] future = ["67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8"] idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"] -importlib-metadata = ["a9f185022cfa69e9ca5f7eabfd5a58b689894cb78a11e3c8c89398a8ccbb8e7f", "df1403cd3aebeb2b1dcd3515ca062eecb5bd3ea7611f18cba81130c68707e879"] +importlib-metadata = ["6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", "cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db"] ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] isort = ["c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a", "f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d"] itsdangerous = ["321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", "b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"] @@ -878,7 +882,7 @@ pyflakes = ["17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", pyinotify = ["9c998a5d7606ca835065cdabc013ae6c66eb9ea76a00a1e3bc6e0cfe2b4f71f4"] pyparsing = ["1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", "9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"] pyrsistent = ["16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a"] -pytest = ["6032845e68a17a96e8da3088037f899b56357769a724122056265ca2ea1890ee", "bea27a646a3d74cbbcf8d3d4a06b2dfc336baf3dc2cc85cf70ad0157e73e8322"] +pytest = ["4a784f1d4f2ef198fe9b7aef793e9fa1a3b2f84e822d9b3a64a181293a572d45", "926855726d8ae8371803f7b2e6ec0a69953d9c6311fa7c3b6c1b929ff92d27da"] pytest-cov = ["2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6", "e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"] python-dateutil = ["7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", "c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"] pytz = ["303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", "d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"] diff --git a/pyproject.toml b/pyproject.toml index 7ba99f3..f3d353c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ authors = ["Kyle King "] [tool.poetry.dependencies] python = "^3.7" -dash = "^0.43.0" +dash = {git = "https://github.com/plotly/dash"} dash-core-components = "^0.48.0" dash-daq = "^0.1.4" dash-html-components = "^0.16.0" diff --git a/requirements.txt b/requirements.txt index 8977c9d..8bca2ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,15 +3,15 @@ attrs==19.1.0 certifi==2019.3.9 chardet==3.0.4 click==7.0 -cloudpickle==1.2.0 +cloudpickle==1.2.1 colorama==0.4.1 coverage==4.5.3 -dash==0.42.0 -dash-core-components==0.47.0 +-e git+https://github.com/plotly/dash@52b473b78082cda0b89afc122882cd5d4c755d66#egg=dash +dash-core-components==0.48.0 dash-daq==0.1.5 dash-html-components==0.16.0 -dash-renderer==0.23.0 -dash-table==3.6.0 +dash-renderer==0.24.0 +dash-table==3.7.0 decorator==4.4.0 doit==0.31.1 entrypoints==0.3 @@ -32,7 +32,7 @@ flask==1.0.3 flask-compress==1.4.0 future==0.17.1 idna==2.8 -importlib-metadata==0.17 +importlib-metadata==0.18 ipython-genutils==0.2.0 isort==4.3.20 itsdangerous==1.1.0 @@ -57,7 +57,7 @@ pyflakes==2.1.1 pyinotify==0.9.6 pyparsing==2.4.0 pyrsistent==0.15.2 -pytest==4.6.2 +pytest==4.6.3 pytest-cov==2.7.1 python-dateutil==2.8.0 pytz==2019.1 @@ -65,7 +65,7 @@ radon==3.0.3 requests==2.22.0 retrying==1.3.3 six==1.12.0 -testfixtures==6.8.2 +testfixtures==6.9.0 traitlets==4.3.2 urllib3==1.25.3 wcwidth==0.1.7