Py.Cafe

acabrera.citizens/

programming_languagues

Dash Style Update Demo

DocsPricing
  • assets/
  • Popularity of Programming Languages from 2004 to 2024.csv
  • app.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
import dash
from dash import dcc, html, Input, Output, State
import dash_bootstrap_components as dbc
import pandas as pd
import plotly.express as px
from flask_caching import Cache

# Initialize app with caching
app = dash.Dash(
    __name__, 
    external_stylesheets=[dbc.themes.JOURNAL],
    # Assets will be automatically loaded from the assets folder
    assets_folder='assets'  
)
app.title = "Programming Languagues Trend"

# Setup cache
cache = Cache(app.server, config={
    'CACHE_TYPE': 'SimpleCache',
})

# Cache timeout in seconds (10 minutes)
TIMEOUT = 600

# Custom color map - moved outside main logic
language_color_map = {
    'Python': '#4B8BBE', 'Java': '#B07219', 'JavaScript': '#F7DF1E',
    'C/C++': '#555555', 'C#': '#178600', 'PHP': '#4F5D95',
    'Ruby': '#CC342D', 'Swift': '#F05138', 'Kotlin': '#A97BFF',
    'Go': '#00ADD8', 'Rust': '#DEA584', 'TypeScript': '#007ACC',
    'R': '#198CE7', 'Perl': '#0298c3', 'Scala': '#c22d40',
    'Haskell': '#5e5086', 'Lua': '#000080', 'Groovy': '#e69f56',
    'Julia': '#a270ba', 'Dart': '#00B4AB', 'Matlab': '#bb92ac',
    'Abap': '#E8274B', 'Ada': '#02f88c', 'Cobol': '#8a1267',
    'Delphi/Pascal': '#8F9EC7', 'Objective-C': '#438eff',
    'Powershell': '#012456', 'VBA': '#867db1', 'Visual Basic': '#945db7'
}

# Cached data loading function
@cache.memoize(timeout=TIMEOUT)
def load_data():
    # Load and preprocess main dataset
    df = (pd.read_csv("Popularity of Programming Languages from 2004 to 2024.csv", 
                     parse_dates=['Date'], date_format='mixed')
          .assign(Year=lambda x: x['Date'].dt.year))
    
    # Load language details
    data = {
        'Language': ['Abap', 'Ada', 'C/C++', 'C#', 'Cobol', 'Dart', 'Delphi/Pascal', 
                    'Go', 'Groovy', 'Haskell', 'Java', 'JavaScript', 'Julia', 
                    'Kotlin', 'Lua', 'Matlab', 'Objective-C', 'Perl', 'PHP', 
                    'Powershell', 'Python', 'R', 'Ruby', 'Rust', 'Scala', 
                    'Swift', 'TypeScript', 'VBA', 'Visual Basic'],
        'Category': ['Enterprise Development', 'Systems', 'Systems', 'Multi-purpose', 
                    'Specific Purpose (Finance)', 'Mobile', 'Desktop', 'Systems', 
                    'Web', 'Functional', 'Multi-purpose', 'Multi-purpose', 'Data', 
                    'Mobile', 'Specific Purpose (Games)', 'Data', 'Mobile', 'Scripting', 
                    'Web', 'Scripting', 'Multi-purpose', 'Data', 'Web', 'Systems', 
                    'Web', 'Mobile', 'Web', 'Scripting', 'Desktop'],
        'Users/Professionals': ['SAP Consultants', 'Systems Engineers', 'Systems Engineers', 
                               'Windows Developers', 'Legacy Systems Programmers', 
                               'Flutter Developers', 'Desktop Developers', 'Systems Engineers', 
                               'Web Developers', 'Researchers', 'Web Developers', 
                               'Web Developers', 'Data Scientists', 'Android Developers', 
                               'Game Developers', 'Engineers', 'iOS Developers', 
                               'System Administrators', 'Web Developers', 'System Administrators', 
                               'Data Scientists', 'Statisticians', 'Web Developers', 
                               'Systems Engineers', 'Web Developers', 'iOS Developers', 
                               'Web Developers', 'Microsoft Office Users', 'Desktop Developers'],
    }
    programs_df = pd.DataFrame(data)
    
    return df, programs_df

# Get data and prepare language options
df, programs_df = load_data()
language_options = [{"label": lang, "value": lang} for lang in df.columns[1:] if lang != 'Year']


# Create language selection modal with improved layout
language_select_modal = dbc.Modal(
    [
        # dbc.ModalHeader("Select Programming Languages"),
        dbc.ModalBody([
            dcc.Checklist(
                id='language-checklist',
                options=language_options,
                value=['Python', 'Java'],  # Better default selection
                inline=True,
                labelStyle={'display': 'inline-block', 'margin-right': '15px', 'margin-bottom': '8px'},
                className="horizontal-checklist"
            ),
            html.Div(id="selected-count", className="mt-2 text-muted")
        ]),
        dbc.ModalFooter([
            dbc.Button("Clear All", id="clear-all", color="secondary", className="me-2"),
            dbc.Button("Close", id="close-modal", color="primary")
        ]),
    ],
    id="language-select-modal",
    size="xl",
)

# Improved app layout with better component organization
app.layout = dbc.Container([
    dbc.Row([
        # Left Sidebar
        dbc.Col([
            html.Div(
                id='language-info-cards',
                className="left-sidebar"
            )
        ], width=12, lg=3, className="mb-4 mb-lg-0"),
        
        # Main content
        dbc.Col([
            html.Div([
                html.H3("Code Chronicles: The Changing Landscape of Programming", 
                       className="mb-3 mt-2", 
                       style={'font-weight': '300', 'color': '#333'}),
                
                html.P("Track the popularity trends of programming languages since 2004",
                      className="text-muted mb-3"),
                
                html.Hr(),
                
                # Language selection controls
                dbc.Row([
                    dbc.Col([
                        dbc.Button(
                            [html.I(className="fas fa-filter me-2"), "Select Programming Languages"], 
                            id="open-modal", 
                            color="primary", 
                            className="mb-3 w-100"
                        )
                    ], width=12, md=6),
                    dbc.Col([
                        dbc.RadioItems(
                            id="chart-view",
                            options=[
                                {"label": "Trends over Years", "value": "line"},
                                {"label": "YoY Relative Change", "value": "bar"}
                            ],
                            value="line",
                            inline=True,
                            className="mb-3"
                        )
                    ], width=12, md=6, className="text-md-end")
                ]),
                
                # Modal for language selection
                language_select_modal,
                
                # Dynamic graph display based on selected view
                html.Div(id="chart-container")
            ]),
        ], width=12, lg=9, className="main-content"),
    ], className="g-0")
], fluid=True)

# Callback for toggling the modal
@app.callback(
    Output("language-select-modal", "is_open"),
    [Input("open-modal", "n_clicks"), Input("close-modal", "n_clicks")],
    [State("language-select-modal", "is_open")],
)
def toggle_modal(n1, n2, is_open):
    if n1 or n2:
        return not is_open
    return is_open

# Callback for Clear All button

@app.callback(
    Output("language-checklist", "value"),
    Input("clear-all", "n_clicks"),
    prevent_initial_call=True
)
def clear_checklist(n_clicks):
    return []

# Callback to show selected count
@app.callback(
    Output("selected-count", "children"),
    [Input("language-checklist", "value")]
)
def show_selection_count(selected):
    count = len(selected) if selected else 0
    return f"{count} languages selected"

# Callback for updating the chart container based on view type
@app.callback(
    Output('chart-container', 'children'),
    [Input('chart-view', 'value'),
     Input('language-checklist', 'value')]
)
def update_chart_container(view_type, selected_languages):
    if not selected_languages:
        return html.Div(
            dbc.Alert("Please select at least one programming language to display data.", 
                     color="info"),
            className="my-5 text-center"
        )
    
    if view_type == "line":
        return dcc.Graph(
            id='language-graph',
            figure=create_line_chart(selected_languages),
            config={'displayModeBar': False},
            className="mb-4"
        )
    else:
        return dcc.Graph(
            id='pct-change-bar-chart',
            figure=create_bar_chart(selected_languages),
            config={'displayModeBar': False},
            className="mb-4"
        )

# Refactored chart creation functions
def create_line_chart(selected_languages):
    data = df.groupby('Year', as_index=False)[selected_languages].mean()
    
    fig = px.line(
        data, 
        x='Year', 
        y=selected_languages, 
        labels={'value': 'Popularity (%)', 'Year': 'Year'}, 
        template='plotly_white',
        markers=True,
        color_discrete_map={lang: language_color_map.get(lang, '#999999') for lang in selected_languages}
    )
    
    fig.update_layout(
        title='Programming Language Popularity Trends',
        title_font_size=16,
        legend_title_text='',
        font=dict(family="Arial, sans-serif", size=12),
        margin=dict(l=40, r=40, t=40, b=40),
        xaxis=dict(showgrid=False),
        yaxis=dict(showgrid=True, gridcolor='#eee', title='Popularity (%)'),
        plot_bgcolor='rgba(0,0,0,0)',
        paper_bgcolor='rgba(0,0,0,0)',
        legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
    )
    
    # Add hover template with better formatting
    for trace in fig.data:
        trace.hovertemplate = '%{y:.1f}%<extra>%{fullData.name} (%{x})</extra>'
    
    return fig

def create_bar_chart(selected_languages):
    # Calculate yearly mean for the selected languages
    yearly_data = df.groupby('Year', as_index=False)[selected_languages].mean()

    # Calculate percentage change across years
    yearly_data_pct_change = yearly_data[selected_languages].pct_change() * 100
    yearly_data_pct_change['Year'] = yearly_data['Year']
    
    # Remove first year (NaN values)
    yearly_data_pct_change = yearly_data_pct_change.dropna()
    
    fig = px.bar(
        yearly_data_pct_change,
        x='Year',
        y=selected_languages,
        title='Year-over-Year Relative Percentage Change',
        labels={'value': 'Relative Change (%)', 'variable': 'Language'},
        barmode='group',
        template='plotly_white',
        color_discrete_map={lang: language_color_map.get(lang, '#999999') for lang in selected_languages}
    )
    
    fig.update_layout(
        title_font_size=16,
        legend_title_text='',
        font=dict(family="Arial, sans-serif", size=12),
        margin=dict(l=40, r=40, t=50, b=40),
        xaxis=dict(showgrid=False, title='Year'),
        yaxis=dict(showgrid=True, gridcolor='#eee', title='Change (%)'),
        plot_bgcolor='rgba(0,0,0,0)',
        paper_bgcolor='rgba(0,0,0,0)',
        legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
    )
    
    # Add a reference line at y=0
    fig.add_shape(
        type="line", line=dict(dash="dot", width=1, color="#666"),
        y0=0, y1=0, x0=yearly_data_pct_change['Year'].min(), 
        x1=yearly_data_pct_change['Year'].max()
    )
    
    # Add hover template with better formatting
    for trace in fig.data:
        trace.hovertemplate = '%{y:.1f}%<extra>%{fullData.name} (%{x})</extra>'
    
    return fig

# Callback for updating cards in the left sidebar
@app.callback(
    Output('language-info-cards', 'children'),
    [Input('language-checklist', 'value')]
)
def update_cards(selected_languages):
    if not selected_languages:
        return html.Div([
            html.H5("Programming Language Details", 
                   style={'margin-bottom': '15px', 'color': '#444', 'font-weight': '300'}),
            html.Hr(),
            html.P("Select languages to see details", 
                  className="text-muted text-center my-4")
        ])

    cards = [
        html.H5("Programming Language Details", 
               style={'margin-bottom': '15px', 'color': '#444', 'font-weight': '300'}),
        html.Hr(),
        html.Div(f"{len(selected_languages)} languages selected", 
                className="text-muted mb-3 small")
    ]
    
    for lang in selected_languages:
        # Get language info or default values
        try:
            lang_info = programs_df[programs_df['Language'] == lang].iloc[0]
            category = lang_info['Category']
            users = lang_info['Users/Professionals']
        except (IndexError, KeyError):
            category = "Unknown"
            users = "Various developers"
        
        # Get language color or default
        lang_color = language_color_map.get(lang, '#666666')
        
        card = dbc.Card(
            dbc.CardBody([
                html.H6(lang, style={'font-weight': '500', 'color': lang_color}),
                html.P(f"Category: {category}", 
                      style={'font-size': '0.8rem', 'color': '#666'}),
                html.P(f"Users: {users}", 
                      style={'font-size': '0.8rem', 'color': '#666'})
            ]),
            style={
                'margin-bottom': '8px',
                'border': 'none',
                'border-left': f'4px solid {lang_color}',
                'border-radius': '0',
                'box-shadow': '0 1px 2px rgba(0,0,0,0.05)'
            },
            className="compact-card"
        )
        cards.append(card)
    
    return cards